并行循环调用中的进度条

我试图在multithreading环境中更新进度条。 我知道很多问题已经解决了这个问题,但提议的解决方案都没有对我有用。 这是我的代码的主干:

public static void DO_Computation(//parameters) { //Intialisation of parameters Parallel.For(struct initialisation with local data) { //business logic //Call to update_progressbar (located in an another class, as the DO_Computation function is in Computation.cs class (not deriving from Form). WinForm.Invoke((Action)delegate {Update_Progress_Bar(i);}); //WinForm is a class that exposes the progressbar. } } 

这不起作用(进度条在达到100%时冻结,这是正常的(我们可以参考这个问题的微软文章 (事实上​​,这不是一个线程安全的操作方法)。)Microsoft网站stiraptes to wrap Parallel.For循环进入Task例程,如下所示:

 public static void DO_Computation(//parameters) { //Intialisation of parameters Task.Factory.StartNew(() => { Parallel.For(struct initialosation with local data) { //business logic //Call to update_progressbar (ocated in an another class, as the DO_Computation function is in Computation.cs class (not deriving from Form). WinForm.Invoke((Action)delegate {Update_Progress_Bar(i);}); //WinForm is a class that exposes the progressbar. .. } }); }); 

但是,当调试线程直接退出Task范围时,这也不起作用。

编辑2:

基本上,我的问题分为3部分: Computation.cs (其中DO_Computation ), WinForm是包含进度条的表单, MainWindow是包含按钮的表单,单击该按钮时打开带有进度条的表单。

我不清楚在这种情况下使用“任务”的用途。 因为它在没有执行任何Parallel.For工作的情况下超出了任务范围

有任何想法吗?

非常感谢,

编辑3:

我在Noseratio的帮助下升级了我的代码(对他来说很多)。 但是我遇到了同样的问题,即任务中的代码永远不会被执行。 我的代码现在看起来像:


DoComputation method //Some Initilasations here Action enableUI = () => { frmWinProg.SetProgressText("Grading Transaction..."); frmWinProg.ChangeVisibleIteration(true); }; Action handleError = (ex) => { // error reporting MessageBox.Show(ex.Message); }; var cts = new CancellationTokenSource(); var token = cts.Token; Action cancel_work = () => { frmWinProg.CancelTransaction(); cts.Cancel(); }; var syncConext = SynchronizationContext.Current; Action progressReport = (i) => syncConext.Post(_ => frmWinProg.SetIteration(i,GrpModel2F.NumOfSim, true), null); var task = Task.Factory.StartNew(() => { ParallelLoopResult res = Parallel.For(1,NbSim, options, () => new DataStruct(//Hold LocalData for each thread), (iSim, loopState, DataStruct) => //Business Logic if (token.IsCancellationRequested) { loopState.Stop(); } progressReport(iSim); //Business Logic return DataStruct; }, (DataStruct) => //Assiginig Results; });//Parallel.For end }, token, TaskCreationOptions.LongRunning, TaskScheduler.Default); task.ContinueWith(_ => { try { task.Wait(); } catch (Exception ex) { while (ex is AggregateException && ex.InnerException != null) ex = ex.InnerException; handleError(ex); } enableUI(); }, TaskScheduler.FromCurrentSynchronizationContext

());

请注意,Do_Computation函数本身是从运行BackGroundWorker的Form调用的。

使用async/awaitProgress并使用CancellationTokenSource观察CancellationTokenSource

一个很好的阅读,相关: “4.5中的异步:在异步API中启用进度和取消” 。

如果您需要以.NET 4.0为目标但使用VS2012 +进行开发,您仍然可以使用async/await ,Microsoft会为此提供Microsoft.Bcl.Async库。

我已经整理了一个WinForms示例,说明了上述所有内容。 它还展示了如何使用ParallelLoopState.Stop()观察Parallel.For循环的取消:

 using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication_22487698 { public partial class MainForm : Form { public MainForm() { InitializeComponent(); } IEnumerable _data = Enumerable.Range(1, 100); Action _cancelWork; private void DoWorkItem( int[] data, int item, CancellationToken token, IProgress progressReport, ParallelLoopState loopState) { // observe cancellation if (token.IsCancellationRequested) { loopState.Stop(); return; } // simulate a work item Thread.Sleep(500); // update progress progressReport.Report(item); } private async void startButton_Click(object sender, EventArgs e) { // update the UI this.startButton.Enabled = false; this.stopButton.Enabled = true; try { // prepare to handle cancellation var cts = new CancellationTokenSource(); var token = cts.Token; this._cancelWork = () => { this.stopButton.Enabled = false; cts.Cancel(); }; var data = _data.ToArray(); var total = data.Length; // prepare the progress updates this.progressBar.Value = 0; this.progressBar.Minimum = 0; this.progressBar.Maximum = total; var progressReport = new Progress((i) => { this.progressBar.Increment(1); }); // offload Parallel.For from the UI thread // as a long-running operation await Task.Factory.StartNew(() => { Parallel.For(0, total, (item, loopState) => DoWorkItem(data, item, token, progressReport, loopState)); // observe cancellation token.ThrowIfCancellationRequested(); }, token, TaskCreationOptions.LongRunning, TaskScheduler.Default); } catch (Exception ex) { MessageBox.Show(ex.Message); } // update the UI this.startButton.Enabled = true; this.stopButton.Enabled = false; this._cancelWork = null; } private void stopButton_Click(object sender, EventArgs e) { if (this._cancelWork != null) this._cancelWork(); } } } 

更新了 ,这里是如何在没有async/await情况下做同样的事情:

 using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication_22487698 { public partial class MainForm : Form { public MainForm() { InitializeComponent(); } IEnumerable _data = Enumerable.Range(1, 100); Action _cancelWork; private void DoWorkItem( int[] data, int item, CancellationToken token, Action progressReport, ParallelLoopState loopState) { // observe cancellation if (token.IsCancellationRequested) { loopState.Stop(); return; } // simulate a work item Thread.Sleep(500); // update progress progressReport(item); } private void startButton_Click(object sender, EventArgs e) { // update the UI this.startButton.Enabled = false; this.stopButton.Enabled = true; Action enableUI = () => { // update the UI this.startButton.Enabled = true; this.stopButton.Enabled = false; this._cancelWork = null; }; Action handleError = (ex) => { // error reporting MessageBox.Show(ex.Message); }; try { // prepare to handle cancellation var cts = new CancellationTokenSource(); var token = cts.Token; this._cancelWork = () => { this.stopButton.Enabled = false; cts.Cancel(); }; var data = _data.ToArray(); var total = data.Length; // prepare the progress updates this.progressBar.Value = 0; this.progressBar.Minimum = 0; this.progressBar.Maximum = total; var syncConext = SynchronizationContext.Current; Action progressReport = (i) => syncConext.Post(_ => this.progressBar.Increment(1), null); // offload Parallel.For from the UI thread // as a long-running operation var task = Task.Factory.StartNew(() => { Parallel.For(0, total, (item, loopState) => DoWorkItem(data, item, token, progressReport, loopState)); // observe cancellation token.ThrowIfCancellationRequested(); }, token, TaskCreationOptions.LongRunning, TaskScheduler.Default); task.ContinueWith(_ => { try { task.Wait(); // rethrow any error } catch (Exception ex) { while (ex is AggregateException && ex.InnerException != null) ex = ex.InnerException; handleError(ex); } enableUI(); }, TaskScheduler.FromCurrentSynchronizationContext()); } catch (Exception ex) { handleError(ex); enableUI(); } } private void stopButton_Click(object sender, EventArgs e) { if (this._cancelWork != null) this._cancelWork(); } } }