マルチスレッド: ワーカー スレッドの生成
ワーカー スレッドでは、主にバックグラウンド タスクを処理します。アプリケーション処理の中で、ユーザーがその終了を待つ必要がない処理はバックグラウンド タスクにできます。再計算やバックグラウンド印刷などはワーカー スレッドの良い例です。このトピックでは、ワーカー スレッドの作成手順について詳しく説明します。ここでは、次の内容について説明します。
スレッドの起動
制御関数の実装
例
ワーカー スレッドの作成は比較的簡単な作業です。スレッドを実行するために必要な手順は、(1) 制御関数の作成、(2) スレッドの起動の 2 つだけです。CWinThread からクラスを派生する必要はありません。CWinThread の専用の派生クラスが必要な場合は派生できますが、通常、単純なワーカー スレッドを実行する場合は必要ありません。CWinThread をそのまま使用できます。
スレッドの起動
AfxBeginThreadの 2 種類のオーバーロードされたバージョンがあります: ワーカー スレッドを作成できる 1 とユーザー インターフェイスの両方のスレッドとワーカー スレッドを作成できる 1。最初のオーバーロードを使用して、ワーカー スレッドの実行を開始するには、次の情報を提供する AfxBeginThreadを呼び出します:
制御関数のアドレス。
制御関数に渡すパラメーター。
(省略可) スレッドの優先順位。既定の優先順位は normal です。優先順位の詳細については、Windows SDK の「SetThreadPriority」を参照してください。
(省略可) スレッドのスタック サイズ。既定値は、このスレッドを生成するスレッドのスタックと同じサイズです。
(省略可) スレッドを作成するときに一時停止状態にするには CREATE_SUSPENDED を渡します。既定値 0 では、スレッドを通常どおり起動します。
(省略可) セキュリティ属性。既定では親スレッドと同じ値になります。このセキュリティ情報の形式の詳細については、Windows SDK の「SECURITY_ATTRIBUTES」を参照してください。
AfxBeginThread 関数は、CWinThread オブジェクトを生成、初期化、および起動し、生成した CWinThread オブジェクトのアドレスを返します。このアドレスは後でプログラムから参照できます。なんらかの原因でスレッド生成に失敗すると、スレッド生成処理全体をチェックし、すべてのオブジェクトを確実に解放します。
制御関数の実装
制御関数によってスレッドを定義します。この関数に入ると、スレッドが起動され、この関数が終了すると、スレッドも終了します。次に、この関数のプロトタイプを示します。
UINT MyControllingFunction( LPVOID pParam );
パラメーターは単一の値です。関数がこのパラメーターで受け取る値は、スレッド オブジェクトを生成したときにコンストラクターに渡した値です。制御関数のデザインしだいで、この値の解釈が異なります。スカラー値として解釈させることも、複数のパラメーターを含めた構造体へのポインターとして解釈させることもできます。また、値を無視することもできます。パラメーターで構造体を参照すると、構造体を使って呼び出し元からスレッドに値を渡すだけでなく、スレッドから呼び出し元に値を渡すこともできます。このようにスレッドから構造体を使って呼び出し元に値を渡すときは、スレッドから呼び出し元に対して戻り値の送出が整ったことを通知します。このようなワーカー スレッドから呼び出し元への通信については、「マルチスレッド : プログラミングのヒント」を参照してください。
制御関数は終了時に、終了理由を示す UINT 型の値を返します。通常は 0 が成功を、その他の値はエラーの種類を示します。戻り値の仕様は純粋に実装に依存します。たとえば、オブジェクトの使用数を管理するスレッドでは、現在の情報を返すことができます。アプリケーションでこの値を取得する方法については、「マルチスレッド : スレッドの終了」を参照してください。
MFC ライブラリで作成されたマルチスレッド プログラムで実行できることには、いくつかの制約があります。これらの制約、およびスレッドの使用上のヒントについては、「マルチスレッド : プログラミングのヒント」を参照してください。
制御関数の例
制御関数を定義し、プログラムの別の部分からこの関数を使用する方法の例を次に示します。
UINT MyThreadProc( LPVOID pParam )
{
CMyObject* pObject = (CMyObject*)pParam;
if (pObject == NULL ||
!pObject->IsKindOf(RUNTIME_CLASS(CMyObject)))
return 1; // if pObject is not valid
// do something with 'pObject'
return 0; // thread completed successfully
}
// inside a different function in the program
.
.
.
pNewObject = new CMyObject;
AfxBeginThread(MyThreadProc, pNewObject);
.
.
.