はじめに
以下の例のように、Parallel.ForEachの内側でコントロールを制御すると、フリーズすることがあります。
フリーズする例
private void button1_Click(object sender, EventArgs e)
{
var range = Enumerable.Range(1, 100);
this.progressBar1.Value = 0;
this.progressBar1.Maximum = 100;
var result = Parallel.ForEach(range, num =>
{
//重い処理
System.Threading.Thread.Sleep(1000);
//コントロールの制御
this.Invoke(new Action(() =>
{
lock(this.progressBar1) {
this.progressBar1.Value += 1;
}
}));
});
while(!result.IsCompleted) ;
}
対処法
MSDNによると、「UI スレッドでの並列ループの実行は避ける」と説明されています。
そこで、以下の様にTaskを使ってParallel.ForEachをUIスレッド以外で実行すると、フリーズしないで動作するようです。
2017/01/06 追加
コメントでlaughterさんが、原因について詳しい解説をして下さっています。
フリーズしないように修正
private async void button1_Click(object sender, EventArgs e)
{
var range = Enumerable.Range(1, 100);
this.progressBar1.Value = 0;
this.progressBar1.Maximum = 100;
await Task.Factory.StartNew(() =>
{
var result = Parallel.ForEach(range, num =>
{
//重い処理
System.Threading.Thread.Sleep(1000);
//コントロールの制御
this.Invoke(new Action(() =>
{
lock(this.progressBar1) {
this.progressBar1.Value += 1;
}
}));
});
while(!result.IsCompleted) ;
});
}