1.はじめに
MFCでスレッドに関するクラスというと、以下があります。
「でも、MessagePump なんて大それたことしないんだけどな~。大げさだな~」って思いました。
次に候補に挙がるのは Win32 API の CreateThread です。
ここにはさらっととんでもないことか書いてあります。
C ランタイム ライブラリ (CRT) を呼び出す実行可能ファイル内のスレッドは、CreateThread と ExitThreadを するのではなく、スレッド管理に _beginthreadex 関数と _endthreadex 関数 使用する必要があります。これには、マルチスレッド バージョンの CRT を使用する必要があります。 CreateThread を使用して作成されたスレッドが CRT を呼び出すと、CRT はメモリ不足の状態でプロセスを終了する可能性があります。
原文でも以下ですから誤翻訳でもなさそうです。
A thread in an executable that calls the C run-time library (CRT) should use the _beginthreadex and _endthreadex functions for thread management rather than CreateThread and ExitThread; this requires the use of the multithreaded version of the CRT. If a thread created using CreateThread calls the CRT, the CRT may terminate the process in low-memory conditions.
一方、std::thread だと以下の点が物足りないと思います
- スレッドが動作中なのかどうかがわからない
- (使うかどうかはともかく)実行したスレッドの戻り値がわからない
考えるのが面倒になってきました。
2. と、いうことで __beginthreadex を元に スレッドクラスを作りました。
まずはヘッダ。
// ThreadBase.h
#pragma once
#include <windows.h>
class CThreadBase
{
public:
CThreadBase();
virtual ~CThreadBase();
HANDLE CreateThread(unsigned int (__stdcall* start_address)(void*),
void* pParam = nullptr, void* pSecurity = NULL,
unsigned int stack_size = 0, unsigned int initflag = 0, unsigned int* thrdaddr = NULL);
void join();
BOOL isThreadRunning();
BOOL GetExitCodeThread(LPDWORD pExitCode);
private:
static unsigned int __stdcall ThreadBaseRun(void* pParam);
unsigned int (__stdcall* m_pFunc)(void*);
void* m_pParam;
HANDLE m_hThread;
};
引数の順番は好みの問題です。セキュリティ記述子などほとんど使ったことがないもの。
そんなのデフォルト引数 にして NULLを入れておけばいいのではないか。で、cpp。
// ThreadBase.cpp
#include "ThreadBase.h"
CThreadBase::CThreadBase()
: m_pFunc(NULL)
, m_pParam(NULL)
, m_hThread(INVALID_HANDLE_VALUE)
{
// no more!
}
CThreadBase::~CThreadBase()
{
join();
}
void CThreadBase::join()
{
if (m_hThread != INVALID_HANDLE_VALUE) {
WaitForSingleObject(m_hThread, INFINITE);
CloseHandle(m_hThread);
m_hThread = INVALID_HANDLE_VALUE;
}
}
BOOL CThreadBase::isThreadRunning()
{
if (m_hThread == INVALID_HANDLE_VALUE) {
return FALSE;
}
return (WaitForSingleObject(m_hThread, 0) == WAIT_TIMEOUT);
}
BOOL CThreadBase::GetExitCodeThread(LPDWORD pExitCode)
{
if (m_hThread == INVALID_HANDLE_VALUE) {
return FALSE;
}
return (::GetExitCodeThread(m_hThread, pExitCode));
}
HANDLE CThreadBase::CreateThread(unsigned int (__stdcall* start_address)(void*),
void* pParam, void* pSecurity, unsigned int stack_size, unsigned int initflag, unsigned int * thrdaddr)
{
// スレッド起動中の再起動を防ぐ
if ((nullptr == start_address) || (m_hThread != INVALID_HANDLE_VALUE))
{
return INVALID_HANDLE_VALUE;
}
HANDLE hRet = (HANDLE)(_beginthreadex(pSecurity, stack_size, ThreadBaseRun,
this, initflag, thrdaddr));
m_hThread = hRet;
m_pFunc = start_address;
m_pParam = pParam;
return hRet;
}
unsigned int __stdcall CThreadBase::ThreadBaseRun(void* pParam)
{
CThreadBase* pThis = static_cast<CThreadBase*>(pParam);
unsigned int (__stdcall * pFunc)(void*) = pThis->m_pFunc;
unsigned int uRet = pFunc(pThis->m_pParam);
_endthreadex(uRet);
// この文は実行されない
return uRet;
}
3. あとはよろしく
CThreadBase を継承して使ってください。