VLT*_*VLT 7 .net c# multithreading task-parallel-library async-await
我试图在多线程环境中更新进度条.我知道很多问题已经解决了这个问题,但提议的解决方案都没有对我有用.这是我的代码的主干:
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.
}
}
Run Code Online (Sandbox Code Playgroud)
这是行不通的(当达到100%时,进度条会冻结,这是正常的(我们可以参考这篇文章中的微软文章(实际上,这不是一种线程安全的操作方法).)Microsoft网站用于包装所述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.
..
}
});
});
Run Code Online (Sandbox Code Playgroud)
但是,当调试线程直接退出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<Exception> 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<int> progressReport = (i) =>
syncConext.Post(_ => frmWinProg.SetIteration(i,GrpModel2F.NumOfSim, true), null);
var task = Task.Factory.StartNew(() =>
{
ParallelLoopResult res = Parallel.For<LocalDataStruct>(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
Run Code Online (Sandbox Code Playgroud)
());
请注意,Do_Computation函数本身是从运行BackGroundWorker的Form调用的.
使用async/await,Progress<T>并观察取消CancellationTokenSource.
一个很好的阅读,相关:"4.5中的异步:在异步API中启用进度和取消".
如果您需要以.NET 4.0为目标但使用VS2012 +进行开发,您仍然可以使用async/await,Microsoft Microsoft.Bcl.Async为此提供了库.
我已经整理了一个WinForms示例,说明了上述所有内容.它还显示了如何观察取消Parallel.For循环,使用ParallelLoopState.Stop():
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<int> _data = Enumerable.Range(1, 100);
Action _cancelWork;
private void DoWorkItem(
int[] data,
int item,
CancellationToken token,
IProgress<int> 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<int>((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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
更新了,这里是如何做到这一点没有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<int> _data = Enumerable.Range(1, 100);
Action _cancelWork;
private void DoWorkItem(
int[] data,
int item,
CancellationToken token,
Action<int> 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<Exception> 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<int> 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();
}
}
}
Run Code Online (Sandbox Code Playgroud)