LoginSignup
5
8

More than 5 years have passed since last update.

Parallel.ForEachの内側でコントロールを制御するとフリーズする

Last updated at Posted at 2016-12-21

はじめに

以下の例のように、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) ;
    });
}
5
8
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
8