调用并行化foreach的问题

我在使用System.Threading.Tasks.Parallel.ForEach时遇到问题。 身体foreach progressBar想要更新。 但Invoke方法有时会冻结。

我将代码附加到prograssbar和Buton的表单中。

private void button1_Click(object sender, EventArgs e) { DateTime start = DateTime.Now; pforeach(); Text = (DateTime.Now - start).ToString(); } private void pforeach() { int[] intArray = new int[60]; int totalcount = intArray.Length; object lck = new object(); System.Threading.Tasks.Parallel.ForEach(intArray, () => 0, (x, loop, count) => { int value = 0; System.Threading.Thread.Sleep(100); count++; value = (int)(100f / (float)totalcount * (float)count); Set(value); return count; }, (x) => { }); } private void Set(int i) { if (this.InvokeRequired) { var result = Invoke(new Action(Set), i); } else progressBar1.Value = i; } 

有时它没有问题,但通常会冻结var result = Invoke (new Action (Set), i) 。 试着解决我的问题。

谢谢。

您的问题是Invoke (以及将Task排队到UI TaskScheduler )都要求UI线程处理其消息循环。 但事实并非如此。 它仍在等待Parallel.ForEach循环完成。 这就是你看到僵局的原因。

如果您希望Parallel.ForEach在不阻塞UI线程的情况下运行,请将其包装到Task ,如下所示:

 private TaskScheduler ui; private void button1_Click(object sender, EventArgs e) { ui = TaskScheduler.FromCurrentSynchronizationContext(); DateTime start = DateTime.Now; Task.Factory.StartNew(pforeach) .ContinueWith(task => { task.Wait(); // Ensure errors are propogated to the UI thread. Text = (DateTime.Now - start).ToString(); }, ui); } private void pforeach() { int[] intArray = new int[60]; int totalcount = intArray.Length; object lck = new object(); System.Threading.Tasks.Parallel.ForEach(intArray, () => 0, (x, loop, count) => { int value = 0; System.Threading.Thread.Sleep(100); count++; value = (int)(100f / (float)totalcount * (float)count); Task.Factory.StartNew( () => Set(value), CancellationToken.None, TaskCreationOptions.None, ui).Wait(); return count; }, (x) => { }); } private void Set(int i) { progressBar1.Value = i; } 

我在看我是如何做到这一点的,这个改变可能对你有所帮助:

在我的构造函数中,我有这一行:

TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();

然后我这样做:

  private void changeProgressBar() { (new Task(() => { mainProgressBar.Value++; mainProgressTextField.Text = mainProgressBar.Value + " of " + mainProgressBar.Maximum; })).Start(uiScheduler); } 

这摆脱了需要使用Invoke,如果您使用Task方法,那么它可以解决您的问题。

我认为这些都在System.Threading.Tasks;