如何在winforms中取消阻止UI的“主要”线程(使用Parallel.Foreach)?

时间:2013-11-23 21:55:08

标签: c# .net multithreading winforms parallel.foreach

使用c#,。NET Framework 4.5,VS 2012

尝试使用Parallel.Foreach

结果有一些UI和按钮的添加方法(方法允许旋转文件夹中的所有图片并保存在另一个地方)

private void ProcessFileParallel()
    {
        string[] files =
            Directory.GetFiles(@"C:\Users\Public\Pictures\Sample Pictures",
            "*.jpg", SearchOption.AllDirectories); //get source folder
        string dirNew = @"C:\modifiedImages"; //new folder
        Directory.CreateDirectory(dirNew); //create dir
        //usage of parallel and lambda
        Parallel.ForEach(files, currfiles =>
        {
            string fileName = Path.GetFileName(currfiles); //get cur name of file
             //GC for Bitmap
             //create new object of Bitmap
             using (Bitmap bitmap = new Bitmap(currfiles))
            {
                bitmap.RotateFlip(RotateFlipType.Rotate180FlipX); //rotating
                bitmap.Save(Path.Combine(dirNew, fileName)); //save as
                 //anonym delegate - used for safety access to UI elements from secondary thread
                this.Invoke((Action)delegate
                {
                    //caption name change for form
                    this.Text = 
                        string.Format("Curr Thread {0}", 
                        Thread.CurrentThread.ManagedThreadId);
                }
                );
            }
        }
        );            
    }

它的工作,但结束后(当所有图片旋转并保存在新的地方,并且UI达到顶部类似Curr Thread 11)主线程被锁定 - 意味着UI未激活 - 无法做任何事情。 问题 - 如何解锁我的UI元素?

2 个答案:

答案 0 :(得分:3)

Parallel.ForEach阻塞线程,直到所有循环完成。如果您希望UI保持响应,则需要在任务中运行它。另见这个问题,基本相同:

Does Parallel.ForEach Block?

答案 1 :(得分:1)

我开始撰写评论,但很快就变得太长了。

首先,我同意ChrisK的答案确实是解决问题的好方法。然而,作为一项学术活动,我认为值得解释为什么问题首先发生。

Parallel.ForEach阻止调用它的线程(在你的情况下是UI线程),直到所有并行操作完成。与此同时,你有其他线程尝试同步调用UI线程上的操作(已经被阻止)。那些调用必须等待UI线程解除阻塞,但是在所有非UI线程完成工作之前它不会解除阻塞(他们不能,因为他们正在等待)。将军。你有一个僵局,你对Parallel.ForEach的号召永远不会完成。

实际上,您可以通过简单地将this.Invoke替换为this.BeginInvoke来解决您的问题,Parallel.ForEach会异步地将工作发布到UI线程,从而允许非UI线程继续前进并最终完成;但是我确实认为通过Task(正如ChrisK所建议的)将对Thread.CurrentThread.ManagedThreadId的实际调用卸载到线程池是一个更好的解决方案。

P.S。在一个不相关的注释中,您始终在UI线程上调用Thread.CurrentThread.ManagedThreadId,因此将始终返回您的UI线程的ID ,这可能是也可能不是执行该线程的线程在图像上工作。如果你想知道哪个实际线程正在进行工作,你必须将{{1}}的返回值存储在委托定义之外,然后关闭它。