マルチスレッドの生成

ここでは、マルチスレッドの中でも比較的簡単なワーカースレッドの生成方法について説明する。

プロジェクトの作成
テスト用のプロジェクトを作成する。ダイアログベースで、名前はCUKMultiThreadとする。 ダイアログにはリストボックスIDC_LIST1を貼り付け、[プロパティ]で「ソート」のチェックを外しておく。 また、CListBox型のDDX変数m_List1を定義すること。

制御関数の作成
ワーカースレッドでは、制御関数がスレッドの定義となる。制御関数が起動されることでスレッドが起動し、 制御関数が終了することでスレッドが終了する。
制御関数は以下のように型が決まっているので、注意すること。 なお引数はLPVOID型になっているため、int型やlong型などの数値を渡したり、構造体へのポインタを渡すこともできる。
	UINT ControlFunction( LPVOID pParam );
制御関数を作成する前に、ここでは制御関数の引数として使用するクラスCUKWorkerParamを作成する。 このクラスはデータメンバとしてCListBoxへのポインタと、CString型の文字列を持つ。 今回作成するプロジェクトではスレッド内でリストボックスへ文字列を追加するために、これらの情報が必要となる。 あとはデータメンバに値を設定するSetParam関数と、それらの値を個別に取得するための関数を定義すればよい。 以下にCUKWorkerParamクラスの定義を示しておく。
	// ワーカースレッド制御関数の引数として使用するクラス
	class CUKWorkerParam
	{
	public:
		CUKWorkerParam() {};
		void SetParam( CListBox* pListBox, CString strString )
		{
			m_pListBox = pListBox;
			m_strString = strString;
		}
		CListBox* GetListBox() { return m_pListBox; }
		CString GetString() { return m_strString; }
	private:
		CListBox* m_pListBox;	// リストボックスへのポインタ
		CString m_strString;	// リストボックスへ追加する文字列
	};
CUKWorkerParamクラスを定義したら、いよいよ制御関数WorkerThreadを定義する。 定義の内容については以下に示すとおりである。まず引数pParamをCUKWorkerParam型のポインタに変換し、 ここからリストボックスへのポインタと文字列を取得する。 そしてスレッド内ではリストボックスへ文字列を5回追加し、処理を終了する。
ちなみに制御関数で正常終了した場合には0を返すようにする。
	// ワーカースレッド制御関数
	UINT WorkerThread( LPVOID pParam )
	{
		CUKWorkerParam* pWorker = (CUKWorkerParam*)pParam;
		CListBox* pListBox = pWorker->GetListBox();
		CString strString = pWorker->GetString();

		for( int i = 0; i < 5; i++ )
			pListBox->AddString(strString);

		return 0;
	};
スレッドの実行
制御関数が作成できたら、スレッドを実行するための実装を記述する。
まずUKMultiThreadDlg.hを開き、以下のように制御関数の引数として使用するオブジェクトを4つ定義する。
	class CUKMultiThreadDlg : public CDialog
	{
	……
	private:
		CUKWorkerParam m_Worker1;
		CUKWorkerParam m_Worker2;
		CUKWorkerParam m_Worker3;
		CUKWorkerParam m_Worker4;
	};
ヘッダーを編集したら、次にソースを修正する。CUKMultiThreadDlg.cppを開き、OnInitDialog関数を次のようにする。
	BOOL CUKMultiThreadDlg::OnInitDialog()
	{
		CDialog::OnInitDialog();

		……
	
		// TODO: 特別な初期化を行う時はこの場所に追加してください。
		m_Worker1.SetParam(&m_List1, "1");
		m_Worker2.SetParam(&m_List1, "2");
		m_Worker3.SetParam(&m_List1, "3");
		m_Worker4.SetParam(&m_List1, "4");
		AfxBeginThread(WorkerThread, &m_Worker1);
		AfxBeginThread(WorkerThread, &m_Worker2);
		AfxBeginThread(WorkerThread, &m_Worker3);
		AfxBeginThread(WorkerThread, &m_Worker4);
	
		return TRUE;  // TRUE を返すとコントロールに設定したフォーカスは失われません。
	}
4つのCUKWorkerParamオブジェクトにパラメータを設定した後、AfxBeginThread関数を使ってスレッドを起動している。
AfxBeginThreadでは、第1引数に制御関数のアドレスを、第2引数に制御関数の引数となるオブジェクトのポインタを設定する。

これでプロジェクトをビルドし、実行してみる。そうすると4つのスレッドはそれぞれ数字(文字列)の1、2、3、4のうち、 SetParam関数で指定されたいずれかの値をリストボックスへ追加する。
注目してほしいのは、それぞれが並行して処理をおこなっているため、 あるスレッドで5回文字列を追加し終わる前に他のスレッドの処理が割り込み、文字列を追加しているということだ。 そしてCPUの使用率などによっては実行するたびにこの順番が入れ替わることもありうる。
(ただし、次のスレッドが走り出す前に前のスレッドの処理が終了した場合にはこの限りではない)

目次へ