调用并行化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;