ワーカースレッドを生成するためのクラス

マルチスレッド化するとメインウインドウに与える影響を少なくしつつバックグラウンドで処理しやすい。ここではATLを利用した環境で簡単にスレッドを生成できるクラス「CDnpThreadImpl」を作成した。

■使用例
#include "DnpThreadImpl.h"

///
///\brief
///	テスト用のスレッドクラス
///
class CTestThread	: public CDnpThreadImpl
{
protected:

	///
	///\brief
	///	スレッドメイン処理
	///
	/// CDnpThreadImpl::ThreadMain_() から呼ばれる
	///ユーザーは呼び出さないこと
	///
	///\param	pParam
	///	CreateThread() で渡したユーザー定義のパラメータ
	///
	virtual	DWORD	ThreadMain(void* pParam)
	{
		ATLTRACE("■スレッド開始\n");

		while(1)
		{
			//処理は無限ループ

			::Sleep(1000);

			// IsAbort() でスレッド中断を検出して関数から抜ける
			if(IsAbort())
				break;
		}

		ATLTRACE("■スレッド終了\n");
		return	0;
	}
};

以上のようにスレッド本体処理用のクラスをCDnpThreadImplから派生する形で作ったら、
CTestThread cThread;

cThread.CreateThread(NULL);
とするだけでワーカースレッドが生成して処理が実行される。処理を中断したい場合は
cThread.AbortThread();
のようにする。
スレッドが完全に終了するまで待ちたい場合は
_cThread.DestroyThread(5000);  //最大5秒待つ
のようにする。


■「DnpCriticalSection.h」の内容
#pragma	once

#include "atlexcept.h"


/*!
 * \brief
 * クリティカルセクションクラス
 * 
 * 例外を処理しているだけで、 CComAutoCriticalSection をそのまま利用。
 * 
 */
class CDnpCriticalSection
{
	CComAutoCriticalSection		_sec;		//!< クリティカルセクション本体
public:


	~CDnpCriticalSection()
	{
		HRESULT	hr;
	
		hr = Unlock();

		ATLASSERT(SUCCEEDED(hr));
	}


	/*!
	 * \brief
	 * ロック
	 * 
	 */
	HRESULT Lock(void)
	{
		HRESULT	hr;

		try
		{
			hr = _sec.Lock();
			if(FAILED(hr))
				return	hr;
		}
		catch(CAtlException& e)
		{
			ATLTRACE("■例外発生(HRESULT=%08X)\n",e.m_hr);
		}

		return	hr;
	}


	/*!
	 * \brief
	 * ロック解除
	 * 
	 */
	HRESULT	Unlock(void)
	{
		HRESULT	hr;

		try
		{
			hr = _sec.Unlock();
			if(FAILED(hr))
				return	hr;
		}
		catch(CAtlException& e)
		{
			ATLTRACE("■例外発生(HRESULT=%08X)\n",e.m_hr);
		}

		return	hr;
	}
};


■「DnpThreadImpl.h」の内容
#pragma	once

#include "DnpCriticalSection.h"

///
///\brief
///		スレッド実装用クラス
///
///■利用例
/*! \code
	//
	{
		CTestThread		_cThread;

		_cThread.CreateThread(NULL);
		_cThread.AbortThread();
		_cThread.DestroyThread(1000);
	}
	//
	class CTestThread	: public CDnpThreadImpl
	{
	protected:

		///
		///\brief
		///	スレッドメイン処理
		///
		/// CDnpThreadImpl::ThradMain_() から呼ばれる
		///ユーザーは呼び出さないこと
		///
		///\param	pParam
		///	CreateThread() で渡したユーザー定義のパラメータ
		///
		virtual	DWORD	ThradMain(void* pParam)
		{
			ATLTRACE("■スレッド開始\n");

			while(1)
			{
				::Sleep(1000);

				// IsAbort() でスレッド中断を検出して関数から抜ける
				if(IsAbort())
					break;
			}

			ATLTRACE("■スレッド終了\n");
			return	0;
		}
	};
///\endcode
*/
///
class CDnpThreadImpl
{
	///
	///\brief
	///		スレッドパラメーター
	///
	///内部利用するのみなのでprivate構造体
	///
	struct	THREADPARAM
	{
		CDnpThreadImpl*	pThis;		//!< このクラスのポインタ
		void*	pParam;				//!< ユーザーが CreateThread() を読んだ時に指定されたパラメータ
	};


	///
	///\attention
	///以下のスレッド中断用フラグとクリティカルセクションは
	///put_bAbort() と get_bAbort() 以外からはアクセスしないこと!
	///
	CDnpCriticalSection	_sec_bAbort;	//!< スレッド中断フラグ用クリティカルセクション
	bool	_bAbort;					//!< スレッド中断用フラグ


protected:
	DWORD	_dwThreadID;			//!< 生成したスレッドのID
	HANDLE	_hThread;				//!< 生成したスレッドのハンドル

private:

	///
	///\brief
	///	スレッド中断用フラグ設定用関数
	///
	///\param[in]	bNew
	///新しいフラグ。trueならスレッド中断開始、falseなら無視
	///
	///\retval	true	設定成功
	///\retval	false	失敗
	///
	bool	put_bAbort(bool bNew)
	{
		HRESULT	hr;

		hr = _sec_bAbort.Lock();
		if(FAILED(hr))
			return	false;

		_bAbort = bNew;
		_sec_bAbort.Unlock();

		return	true;
	}


	///
	///\brief
	///	スレッド中断用フラグ取得用関数
	///
	///\param[out]	bValue
	///取得した中断フラグ。trueならスレッド中断処理中もしくは中断終了
	///
	///\retval	true	取得成功
	///\retval	false	失敗
	///
	bool	get_bAbort(bool& bValue)
	{
		HRESULT	hr;

		hr = _sec_bAbort.Lock();
		if(FAILED(hr))
			return	false;

		bValue = _bAbort;
		_sec_bAbort.Unlock();

		return	true;
	}



protected:

	///
	///\brief
	///	スレッド中断中(もしくは中断終了した)かどうか
	///
	///\retval	true	中断中
	///\retval	false	それ以外
	///
	///生成したスレッド内で中断処理をする必要があるかどうかを
	///チェックするのに利用する。
	///
	virtual	bool	IsAbort(void)
	{
		bool	bValue;

		if(get_bAbort(bValue))
			return	bValue;

		//取得に失敗したときはabortフラグfalseとして処理
		return	false;
	}


public:

	CDnpThreadImpl()
	{
		_dwThreadID = 0;
		_hThread = 0;
		put_bAbort(false);
	}

	virtual	~CDnpThreadImpl()
	{
		////スレッドが破棄されていなければASSERT
		//ATLASSERT(_dwThreadID == 0 && _hThread == 0);

		DestroyThread(2000);			//スレッド終了を最大2秒待つ
	}



	///
	///\brief
	///	スレッド中断処理開始用
	///
	///スレッドを中断したいときに呼び出す
	///
	///\retval	true	設定成功
	///\retval	false	失敗
	///
	virtual	bool	AbortThread(void)
	{
		return	put_bAbort(true);
	}


	///
	///\brief
	///	スレッドの終了を待つ
	///
	///生成したスレッドからは呼ばないこと
	///
	///\param[in]	dwWait
	///	終了を待つ最大時間(単位:ミリ秒)\n
	///	INFINITE なら無限に待つ
	///
	///\retval	true	スレッドは終了した
	///\retval	false	スレッド終了待ちに失敗
	///
	bool	WaitUntilThreadEnd(DWORD dwWait=INFINITE)
	{
		DWORD	dwRet;

		if(_hThread == NULL)
			return	true;

		dwRet = ::WaitForSingleObject(_hThread,dwWait);
		if(dwRet == WAIT_TIMEOUT)
			return	false;

		return	true;
	}


	///
	///\brief
	///		スレッド破壊処理
	///
	///スレッド中断フラグを立ててスレッドが終了するのを待つ
	///
	///生成したスレッドからは呼ばないこと
	///
	///\param[in]	dwWait
	///	終了を待つ最大時間(単位:ミリ秒)\n
	///	INFINITE なら無限に待つ
	///
	///\retval	true	スレッドは破壊された
	///\retval	false	失敗
	///
	bool	DestroyThread(DWORD dwWait=INFINITE)
	{
		if(_hThread == NULL)
			return	true;

		//スレッド強制終了フラグを立てる
		AbortThread();

		WaitUntilThreadEnd(dwWait);

		if(IsThreadRunning() == false)
		{
			::CloseHandle(_hThread);
			_hThread	= NULL;
			_dwThreadID	= 0;

			return	true;
		}

		return	false;
	}



	///
	///\brief
	///		スレッドが現在実行中かどうか
	///
	///生成したスレッドからは呼ばないこと
	///
	///\retval	true	実行中
	///\retval	false	実行していない
	///
	bool	IsThreadRunning(void)	const
	{
		BOOL	ret;
		DWORD	dwCode;

		if(_hThread == NULL)
			return	false;

		ret = ::GetExitCodeThread(_hThread,&dwCode);
		if(ret == FALSE)
			return	false;

		return	(dwCode == STILL_ACTIVE) ? true : false;
	}





	///
	///\brief
	///		スレッド生成処理
	///
	///\param[in]	pParam
	///	スレッドに渡したいユーザー定義パラメーター。自由に利用していい
	///
	///\return
	///		ゼロなら失敗。ゼロ以外なら成功
	///
	DWORD	CreateThread(void* pParam)
	{
		ATLASSERT(_dwThreadID == 0 && _hThread == 0);

		THREADPARAM*	pData;

		pData = new THREADPARAM;
		pData->pParam	= pParam;
		pData->pThis	= this;

		put_bAbort(false);
		_hThread = ::CreateThread(NULL,0,ThreadMain_,pData,0,&_dwThreadID);
		if(_hThread == NULL)
		{
			delete	pData;
			return 0;
		}

		return _dwThreadID;
	}



private:

	///
	///\brief
	///		スレッドメイン関数
	///
	///APIの CreateThread から呼ばれる。それ以外からは利用されない。
	///
	///内部で仮想関数の ThreadMain() を呼びだしている
	///
	static DWORD WINAPI ThreadMain_(void* pParam)
	{
		THREADPARAM* pData = (THREADPARAM*) pParam;

		CDnpThreadImpl*	pThis = pData->pThis;
		void*	pUseParam = pData->pParam;

		delete	pParam;

		return	pThis->ThreadMain(pUseParam);
	}


	///
	///\brief
	///	スレッドメイン処理(仮想関数のため実装なし)
	///
	///\param	pParam
	///	CreateThread() の引数として渡されたユーザー定義のパラメータ
	///
	virtual	DWORD	ThreadMain(void* pParam) = 0;
};

プロジェクトファイルをダウンロード


カテゴリー「その他」 のエントリー