如何实现多线程和并行执行多个任务?

3

我对线程编程很陌生。我需要在后台并行运行一些任务(以便主UI执行线程对用户操作保持响应),并等待它们每个都完成后再继续执行。

就像这样:

foreach(MyTask t in myTasks)
{
  t.DoSomethinginBackground(); // There could be n number of task, to save 
                               // processing time I wish to run each of them 
                               // in parallel
}

// Wait till all tasks complete doing something parallel in background


Console.Write("All tasks Completed. Now we can do further processing");

我理解有很多方法可以实现这个目标。但我正在寻找在 .Net 4.0 (C#) 中实现最佳解决方案。


你如何实现 MyTask? - cuongle
好的,MyTask可以是任何东西。这里并不重要。在我的实际解决方案中,我有一个URL数组,我必须为每个URL获取HTML(Web Scrapping)。 - Aakash
3个回答

8

对我来说,你似乎想要使用Parallel.ForEach

Parallel.ForEach(myTasks, t => t.DoSomethingInBackground());

Console.Write("All tasks Completed. Now we can do further processing");

您也可以在单个循环内执行多个任务。

List<string> results = new List<string>(myTasks.Count);
Parallel.ForEach(myTasks, t =>
{
    string result = t.DoSomethingInBackground();
    lock (results)
    { // lock the list to avoid race conditions
        results.Add(result);
    }
});

为了保持主UI线程的响应性,您需要使用BackgroundWorker,并订阅其DoWorkRunWorkerCompleted事件,并在其中调用。
worker.RunWorkerAsync();
worker.RunWorkerAsync(argument); // argument is an object

谢谢Nolonar,目前看起来是个不错的解决方案。但是如果每个任务都返回一个值(比如字符串值),在继续之前我必须合并每个任务的返回字符串,怎么办呢?换句话说,每个任务是否可以在其他任务仍在忙碌时完成并返回值? - Aakash
是的,那正是我所想的。我创建了一个BackGroundWorker实例,在DoWork()方法中使用PLINQ并行运行每个任务。每个任务返回一个字符串集合,我使用AddRange()方法将其存储在全局字符串集合中。 - Aakash
想知道是否可以将DoWork中的值作为参数发送到RunWorkerCompleted事件。比如,如果我在DoWork中声明一个局部变量,并将其传递给RunWorkerCompleted? - Aakash
@Aakash 你可以将一个对象传递给 RunWorkerAsync,例如 worker.RunWorkerAsync(t),参数将作为对象出现在 DoWorkEventArgs.Argument 中,因此你需要进行强制类型转换,如 MyTask t = (MyTask)e.Argument。你还可以使用 DoWorkEventArgs 类来将结果放入其中,参见 BackgroundWorker Examples, MSDN - Nolonar
感谢Nolonar和所有回答这个问题的人。 - Aakash
显示剩余2条评论

1
你可以使用 Task 库来完成:
 string[] urls = ...;
 var tasks = urls.Select(url => Task.Factory.StartNew(() => DoSomething(url)));

为了避免锁定UI线程,你可以在.NET 4.0中使用ContinueWhenAll
Task.Factory.ContinueWhenAll(tasks.ToArray(), _ => 
    Console.Write("All tasks Completed. Now we can do further processing");
);

如果您使用的是最新版本的.NET,您可以使用Task.WhenAll

我认为第二个代码块应该在主线程之外的其他线程上运行。 - Aakash

0

谢谢Huy,我一定会的,看来是时候深入研究多线程了。迟早都得学会。;-) - Aakash

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接