とまと あんらいぷ…

エンジニアの活動記録とかつぶやきとか

GitHub
スポンサードリンク

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

スポンサードリンク

Parallel.For()とInvoke()またはBeginInvoke()を使うとデッドロックする

マルチスレッドでのUI操作における
UIスレッドのデットロックについてハマることがあったので纏めた。

非同期操作、かつ並列処理で、UI上のテキストボックスにテキストを追加する検証を行っていて、
特定の非同期操作でUIスレッドのデッドロックが発生していた。

Parallelで非同期処理を行って、TextBoxに結果を描画させたかったんだけど
Invokeを使えどBeginInvokeを使えど、とにかくロックする。
ちゃんと同期させてるやん!!っていう認識なんだけど、どうやら違ったらしい。

DOBONの掲示板の皆様に感謝
DOBON.NETプログラミング道掲示板


最終的なMSの説明は以下

引用
http://msdn.microsoft.com/ja-jp/library/dd997392.aspx

次の例では、並列ループが実行されている UI スレッドは、すべての反復処理が完了するまでループによってブロックされます。ただし、このループの反復処理がバックグラウンド スレッドで実行されると (For と同じ処理を行う)、Invoke の呼び出しによって UI スレッドにメッセージが送信され、そのメッセージが処理されるのを待機することになります。For を実行中の UI スレッドはブロックされているので、メッセージが処理されることはなく、UI スレッドでデッドロックが発生します。

private void button1_Click(object sender, EventArgs e)
{
 Parallel.For(0, N, i =>
 {
  // do work for i
  button1.Invoke((Action)delegate { DisplayProgress(i); });
  });
}

タスク インスタンス内でループを実行することによってこのデッドロックを避ける方法を次の例に示します。UI スレッドはループによってブロックされず、メッセージを処理できます。
private void button1_Click(object sender, EventArgs e)
{
  Task.Factory.StartNew(() =>
  Parallel.For(0, N, i =>
  {
   // do work for i
   button1.Invoke((Action)delegate { DisplayProgress(i); });
  })
  );
}



UIスレッドをブロックするステートメント内で、UIスレッドにメッセージを投げて待機するような処理を行うな。(UIスレッドはメッセージを処理できる状態にしておけ)
ってことやね。

引用コードはTask.Factoryを使ってるけど
new Thread() を使ってStart()メソッドを使っても問題なさそう。

こんな感じで。

 
private void button1_Click( object sender, EventArgs e )
{
//マニュアルスレッド・バックグラウンドでパラレル処理実行
var t = new Thread( () => {
Parallel.For( 0, 20, id => {
AddMsgUseInvoke( id + "開始" );
System.Threading.Thread.Sleep( 2000 );
AddMsgUseInvoke( id + "終了" );
} );
} );
t.IsBackground = false;
t.Start();
}

///
/// Invokeを使ったTextBox再描画処理。
///

///
private void AddMsgUseInvoke( string msg )
{
//というか、パラレル処理上での描画更新invokeの実例がどこにもない。
//2つの並列処理でもデッドロックした。使わない方がよさそう・・・
textBox1.Invoke( new MethodInvoker( () =>
{
textBox1.Text += msg + " - 1" + "\r\n";
textBox1.Text += msg + " - 2" + "\r\n";
textBox1.Text += msg + " - 3" + "\r\n";
textBox1.Text += msg + " - 4" + "\r\n";
textBox1.Text += msg + " - 5" + "\r\n";
textBox1.Refresh();
}
) );
}


ボツコードをたくさん生成してしまったけどそれは割愛してアップ。
とにかく、InvokeとParallelを使うときは必ず、別スレッドを立てること。
これが大事。
▼この記事を読んだ方は、こんな記事も読んでいます。▼

スポンサードリンク

テーマ:プログラミング - ジャンル:コンピュータ

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバック URL
http://dalmore.blog7.fc2.com/tb.php/35-b1caa59b
この記事にトラックバックする(FC2ブログユーザー)

FC2Ad

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。