- PR -

C++ スレッド間の排他制御について

投稿者投稿内容
est
会議室デビュー日: 2007/08/03
投稿数: 12
投稿日時: 2008-04-23 18:18
現在、スレッドを使用して、2つの処理(AとB)を行うプログラムを作成しています。
しかし、排他制御のところで悩んでおります。
内容は、以下のとおりです。

1. 必ず処理Bの後に、処理Aを行う。
2. メイン関数で処理Aを、スレッド内で処理Bを行う。
3. メイン関数、スレッドともに無限ループを作成し、処理は無限ループがブレークするまで半永久的に行う。

やりたいこととしては、以下のイメージ図のように、
「n回目の処理Aが終了するまでに、n+1回目の処理Bが終了しても、n+2回目以降の処理Bに進まないようにしたい(待たせたい)」
と考えています。

[イメージ図]
()内は回数を表す
-----------------------------------------------------------------------------------------------
| 処理B(1) | 処理B(2) | 処理B(3) | 処理B(4) | 処理B(5) | 処理B(6) | 処理B(7) | …
-----------------------------------------------------------------------------------------------
……………… | 処理A(1) | 処理A(2) | 処理A(3) | 処理A(4) | 処理A(5) | 処理A(6) | 処理A(7) | …
-----------------------------------------------------------------------------------------------

現在、以下に示すコードのように作成しましたが、
処理Aが処理Bより早く完了してしまうため、処理Bの終わらないうちに処理Aが何度も行われるという状況になっています。
ミューテックスを排他制御に問題があるとは思いますが、どのように制御してよいかわかりません。
排他制御についてご教授いただきたく、よろしくお願いします。


最後に、当方の環境は以下のとおりです。
CPU: Genuine Intel(R) CPU T2500 @ 2.00GHz
メモリ: 1GB
OS: Windows XP
開発環境: Microsoft Visual Studio .NET 2003
( C++ コンソールアプリケーション )

---------------------------------------
以下、ソースコード

// メイン関数
int _tmain()
{
m_iLoopFlag = 1;

// ミューテックス生成
m_hMutex = CreateMutex(NULL,FALSE,NULL);
m_hEvent2 = CreateEvent(NULL,TRUE,TRUE,NULL); // イベント作成
SetEvent(m_hEvent2);

// スレッドBを立ち上げる
m_uiThreadFlag = _beginthreadex(NULL, 0, [スレッドB], &param, 0, &thID);

while(m_iLoopFlag>0)
{
// スレッド終了まで待つ
if( WaitForSingleObject( m_hEvent,INFINITE ) == WAIT_OBJECT_0 )
OutputDebugString("WAIT_OBJECT_0\\n");
else if( WaitForSingleObject( m_hEvent,INFINITE ) == WAIT_ABANDONED )
OutputDebugString("WAIT_ABANDONED\\n");
else if( WaitForSingleObject( m_hEvent,INFINITE ) == WAIT_TIMEOUT )
OutputDebugString("WAIT_TIMEOUT\\n");


//---------------
// 処理A
//---------------
OutputDebugString("★ 処理Aスタート ★\\n");
ResetEvent(m_hEvent2);


// ここに処理Aの内容が入ります

SetEvent(m_hEvent2);
OutputDebugString("★ 処理A終了 ★\\n");

// 分割フラグをインクリメント
m_uiCutNo++;
}

// 受信スレッドが終了した事を確認する
if( WaitForSingleObject( (HANDLE)m_uiThreadFlag, INFINITE ) == WAIT_OBJECT_0 )
OutputDebugString("受信スレッド WAIT_OBJECT_0\\n");
else if( WaitForSingleObject( (HANDLE)m_uiThreadFlag, INFINITE ) == WAIT_ABANDONED )
OutputDebugString("受信スレッド WAIT_ABANDONED\\n");
else if( WaitForSingleObject( (HANDLE)m_uiThreadFlag, INFINITE ) == WAIT_TIMEOUT )
OutputDebugString("受信スレッド WAIT_TIMEOUT\\n");

ReleaseMutex(m_hMutex);

return 0;
}

// スレッドB
unsigned int __stdcall [スレッドB](void* arg_pvDummy)
{
OutputDebugString("*** スレッドB スタート ***\\n");

// イベントを作る
m_hEvent = CreateEvent(NULL,TRUE,TRUE,NULL); // イベント作成

while(m_iLoopFlag)
{
// スレッド終了まで待つ
if( WaitForSingleObject( m_hEvent2,INFINITE ) == WAIT_OBJECT_0 )
OutputDebugString("●WAIT_OBJECT_0\\n");
else if( WaitForSingleObject( m_hEvent2,INFINITE ) == WAIT_ABANDONED )
OutputDebugString("●WAIT_ABANDONED\\n");
else if( WaitForSingleObject( m_hEvent2,INFINITE ) == WAIT_TIMEOUT )
OutputDebugString("●WAIT_TIMEOUT\\n");

//---------------
// 処理B
//---------------
OutputDebugString("☆ 処理Bスタート ☆\\n");

ResetEvent(m_hEvent);

// ここに処理Bの内容が入ります

// イベントをセット
SetEvent(m_hEvent);
OutputDebugString("☆ 処理B終了 ☆\\n");
}

OutputDebugString("*** スレッドB 終了 ***\\n");
_endthread();
return 0;
}
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2008-04-23 18:40
「必ずBの後にA」なら、AとBをスレッドで並走させる必要が無いのでは?
levin
会議室デビュー日: 2004/09/02
投稿数: 5
投稿日時: 2008-04-23 18:41
ちょっと良く調べてなくて申し訳ないですが。
 
確か Mutex はプロセス間排他じゃなかったかと。
スレッド間排他を行うのであれば、CriticalSection を使うのが一般的ではないかと頭をよぎりました。
est
会議室デビュー日: 2007/08/03
投稿数: 12
投稿日時: 2008-04-23 19:02
>Jitta さま
ご回答ありがとうございます。
説明が足りなくてすみません。
処理Bでは、ネットワークカメラから送信されてくる映像ストリームを受信しているので
できるだけ取りこぼしのないようにしたいと考えてこのようなスレッドを立てています。

また、処理Aでは、処理Bで受信した映像を分析しています。
分析の最中にも、ネットワークカメラから映像ストリームは送られてくるので、出来るだけ続けて受信するようにしたいと考えています。

>lebin さま
ご回答ありがとうございます。
CriticalSectionについては、こちらも良く調べていませんので調べてみます。
otf
ベテラン
会議室デビュー日: 2006/08/04
投稿数: 91
投稿日時: 2008-04-23 19:33
疑似コードで申し訳ありませんが
こんなんでどうでしょうか?
コード:
void b();

void a()
{
	// 受信
	// a_queueにpush
}

// スレッドA
void receive()
{
	while(!end)
	{
		lock l(cs);
		a();
	}
}

// スレッドB
void process()
{
	while(!end)
	{
		if(!a_queue.empty())
		{
			lock l(cs);
			b();
			a_queue.pop();
		}
	}
}


あとはCriticalSectionとRAIIについて調べてみるといいと思います。
スフレ
ぬし
会議室デビュー日: 2005/05/27
投稿数: 281
お住まい・勤務地: 東京
投稿日時: 2008-04-23 21:54
estさんがしたいことは排他制御ではなく、同期制御です。
「producer-consumer パターン」という確立された手法があるので調べてみてください。
otf
ベテラン
会議室デビュー日: 2006/08/04
投稿数: 91
投稿日時: 2008-04-23 22:29
恥ずかしながら私も今調べたのですが
スフレさんの仰る「producer-consumer パターン」
のほうが適切だと思いました。

すみませんが前回の発言は無視してください。
est
会議室デビュー日: 2007/08/03
投稿数: 12
投稿日時: 2008-04-24 17:50
>otf さま
擬似コードまで考えてくださいまして、有難うございます。

>スフレ さま
ヒントをいただき有難うございます。
(排他制御⇒同期制御でした。誤って認識していました。)
Producer-Consumerパターンについて調べて、実装してみました。
Produce側(動画ストリーム受信)のループ回数が多く回ってしまうので
動画ストリーム受信完了後にSleep文を挟んでConsumer側(動画ストリーム分割処理)とのバランスをとるようにしました。


スキルアップ/キャリアアップ(JOB@IT)