我们假设我有这段代码。
foreach(string value in myList)
{
string result = TimeConsumingTask(value);
//update UI
listBox.Add(result);
}
我想将string result = TimeConsumingTask(value);
移到某个后台线程。如果
- Order does matter
- Order doesn't matter
答案 0 :(得分:3)
在诸如WPF,WinForms,Silverlight等GUI应用程序中,您可以使用BackgroundWorker来处理主线程上发生的不同回调,这在更新UI时非常重要。在ASP.NET中,您可以查看asynchronous pages。
如果订单与后台工作人员有关,那就让我们举个例子:
var worker = new BackgroundWorker();
worker.DoWork += (obj, args) =>
{
args.Result = myList.Select(TimeConsumingTask).ToList();
};
worker.RunWorkerCompleted += (obj, args) =>
{
var result = (IList<string>)args.Result;
foreach(string value in result)
{
listBox.Add(result);
}
};
worker.RunWorkerAsync();
答案 1 :(得分:2)
保证顺序的一种简单方法是在同一个后台线程中按顺序执行所有TimeConsumingTasks。
这可能比在多个线程上执行多个TimeConsumingTasks稍长一些,但是在尝试同步结果并按所需顺序生成输出时会让您省去很多麻烦。
如果您的主要目标是释放UI以便用户可以继续处理其他内容,那么使用一个线程和多个线程之间的完成时间差异可能可以忽略不计,因此请使用最简单的路径 - 使用单个后台线程对于所有TimeConsumingTasks。
如果您的主要目标是使TimeConsumingTasks的计算更快完成,那么运行多个线程可能是更好的方法,因为它更有可能利用多个核心并并行执行某些工作。
要保留多线程案例中的顺序,您必须自己订购结果。您可以维护累加器临时列表,并在结果在列表中的适当位置插入每个结果(如果位置/索引很容易计算或已知),或者在所有线程完成后再添加一个额外的处理步骤来合并多个结果以所需顺序进入最终输出。在并行和分布式计算圈中,将问题分成多个并行完成然后将结果组合成连贯顺序的过程称为“地图缩减”。
答案 2 :(得分:1)
在更新UI之前必须等待结果,因此将TimeConsumingTask()仅移动到后台线程是没有意义的。您需要从后台线程更新UI(但是将调用编组到UI线程)。
这取决于你的最终目标,但一个简单的解决方案就是开始任务。如果订单很重要,这将有效。
Task.Factory.StartNew(
() =>
{
foreach(string value in myList)
{
string result = TimeConsumingTask(value);
listBox.Invoke(new Action(() => listBox.Add(result)));
}
});
使用BackgroundWorker也很简单,但不是“简单”,因为有更多的代码:
答案 3 :(得分:1)
有很多方法可以给这只猫上皮。在过去的几年里,我使用了很多,BackgroundWorker
是最常见和最直接的。但是从现在开始,您必须认为Async CTP是可行的方法。决定前至少review the intro。在简洁和简洁的代码方面,它是有史以来最热门的代码。 :)
答案 4 :(得分:1)
我会使用一个任务。这样整个操作在后台运行而不会阻止UI。但是,请确保更新主线程上的列表框。我不确定如何在WinForms中执行此操作,但在WPF中,您可以使用Dispatcher.Invoke()
或Dispatcher.BeginInvoke()
完成此操作。
如果订购事项:
Task.Factory.StartNew(
() =>
{
foreach (string value in myList)
{
string result = TimeConsumingTask(value);
//update UI
Application.Current.Dispatcher.BeginInvoke(
(Action)(() => listBox.Add(result)));
}
});
如果排序无关紧要,您也可以使用Parallel.ForEach()
。
Task.Factory.StartNew(
() =>
{
Parallel.ForEach(myList,
value =>
{
string result = TimeConsumingTask(value);
//update UI
Application.Current.Dispatcher.BeginInvoke(
(Action)(() => listBox.Add(result)));
});
});
}
答案 5 :(得分:0)
如果顺序无关紧要,您可以使用Parallel.ForEach
循环。 (根据dthorpe和ttymatty的评论编辑)。
using System.Threading.Tasks;
var results = new List<string>();
Parallel.ForEach(myList, value =>
{
string result = TimeConsumingTask(value);
results.Add(result);
});
//update UI
listBox.AddRange(results);
如果您想使用流程信息回调用户界面,我建议使用BackgroundWorker作为Darin Dimitrov在他的回答中推荐的。
答案 6 :(得分:0)
多线程和后台处理之间存在很大差异,但您可以同时使用它们。
使用后台处理 - 使用BackgroundWorker作为Darin建议 - 您可以允许用户继续在UI中工作,而无需等待后台进程完成。如果用户必须等待处理完成才能继续,那么在后台运行它是没有意义的。
使用多线程 - 可能使用Parallel Extensions Library - 您可以使用多个线程并行运行重复循环,以便更快地完成。如果您的用户在继续之前等待该过程完成,这将是有益的。
最后,如果你在后台运行某些东西,你可以通过使用多线程来更快地完成它。