最近决定编写一个“快速”Windows窗体应用程序来标记我的MP3文件。自从.Net 3.0以来没有做任何并行性的事情,所以我正在研究Parallel.ForEach方法来处理我在使用标准foreach语句时得到的UI锁定。这是一段摘录:
var i = 1;
var files = new List<string>(); // File list is populated using recursive method.
foreach(var f in files) {
// Add a row
var row = dgvList.Rows[dgvList.Rows.Add()];
// Update label
lblSummary.Text = string.Concat("Processing... ", i);
// Do things with row
// Increment progress bar
progressBar.PerformStep();
i++;
}
我已经弄清楚了Parallel.ForEach()的简单用法,但我不确定我应该使用该特定方法来更新UI?有什么建议吗?
答案 0 :(得分:0)
你应该非常小心线程安全。 您应确保锁定正在使用的任何对象,并将其正确解锁。
否则,我知道使用Parallel.ForEach for UI应该没有问题。
编辑:您可以设置Form.CheckForIllegalCrossThreadCalls = false以禁用线程安全检查。
以下是一些文档:http://msdn.microsoft.com/en-us/library/system.windows.forms.control.checkforillegalcrossthreadcalls.aspx
这样可行,但这很危险,因为那时你需要自己关心你的线程安全。
处理这个问题的一个更好的方法是使用UI逻辑的invoke-pattern,但是并行性将受到影响,因为UI操作本身将在UI线程上调用。
然而,这是安全的做事方式
文档:http://msdn.microsoft.com/en-us/library/ms171728.aspx
答案 1 :(得分:0)
您不应该使用UI线程中的并行库。并行库在多个线程上运行一组任务,因此您不应在其中编写任何与UI相关的代码。
您应该做的是将业务逻辑移至后台任务,并使用将在UI线程上执行它的调度程序更新UI
正如MSDN所说
It is important to keep your application's user interface (UI) responsive. If an
operation contains enough work to warrant parallelization, then it likely should not
be run that operation on the UI thread. Instead, it should offload that operation to
be run on a background thread. For example, if you want to use a parallel loop to
compute some data that should then be rendered into a UI control, you should consider
executing the loop within a task instance rather than directly in a UI event handler.
Only when the core computation has completed should you then marshal the UI update back
to the UI thread.
最重要的是,如果您尝试从Paralle.Foreach
更新UI线程If you do run parallel loops on the UI thread, be careful to avoid updating UI
controls from within the loop. Attempting to update UI controls from within a parallel
loop that is executing on the UI thread can lead to state corruption, exceptions,
delayed updates, and even deadlocks, depending on how the UI update is invoked
答案 2 :(得分:0)
好的,我发现实现这一目标的最佳方法是运行类似的东西:
// Kick off thread
Task.Factory.StartNew(delegate{
foreach(var x in files) {
// Do stuff
// Update calling thread's UI
Invoke((Action)(() => {
progressBar.PerformStep();
}));
}
}
我实际上更新了我的代码以在foreach循环中填充List,然后通过.DataSource将其分配给daragrid,而不是直接使用.Rows集合。应该从一开始真的这样做:)