在C#中同时运行多个方法的方法

5
我有一个返回XML元素的方法,但是这个方法需要一些时间才能完成并返回值。 现在我有的是:
foreach (var t in s)
{
    r.add(method(test));
}
但是这只会在前一个语句完成后运行下一个语句。我该如何使它同时运行?

raddmethodtest是否线程安全? - Yahia
4个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
13
您可以使用任务来实现此功能:
//first start a task for each element in s, and add the tasks to the tasks collection
var tasks = new List<Task>();
foreach( var t in s)
{
    tasks.Add(Task.Factory.StartNew(method(t)));
}

//then wait for all tasks to complete asyncronously
Task.WaitAll(tasks);

//then add the result of all the tasks to r in a treadsafe fashion
foreach( var task in tasks)
{
  r.Add(task.Result);
}

编辑 上述代码存在一些问题。请查看下面的代码,这是一个可用的版本。在这里,我还重写了循环以使用LINQ以解决可读性问题(在第一个循环中,避免了lambda表达式内部对t的闭包导致的问题)。

var tasks = s.Select(t => Task<int>.Factory.StartNew(() => method(t))).ToArray();

//then wait for all tasks to complete asyncronously
Task.WaitAll(tasks);

//then add the result of all the tasks to r in a treadsafe fashion
r = tasks.Select(task => task.Result).ToList();

错误1:'System.Threading.Tasks.TaskFactory.StartNew(System.Action)' 的最佳重载方法匹配存在一些无效参数,我在 tasks.Add(Task.Factory.StartNew(method(t))) 上不断收到错误提示。 - ikel
我看到我的代码有一些问题。给我两分钟时间为您编辑它。 - Øyvind Bråthen
看,这是我的编辑。代码也被清理了一下 :) - Øyvind Bråthen
谢谢你的帮助,但是我在最后一行仍然遇到了关于task=>task.Result的错误,请问你的代码中定义了task吗? - ikel
你确定第一行使用的是 Task<int> 而不仅仅是 Task 吗?在我的机器上,这段代码可以编译并正常工作。 - Øyvind Bråthen

7

您可以使用Parallel.ForEach,它将利用多个线程并行执行。您必须确保所有调用的代码都是线程安全的,并且可以在并行环境下执行。

Parallel.ForEach(s, t => r.add(method(t));

这是一些新东西,我以前从未使用过,我会尝试并回来这里,非常感谢。 - ikel
6
这基本上是正确的,但问题在于对 r 进行添加操作的方法(我预期 r 是一个列表或类似结构)不是线程安全的,如果从许多不同的线程向列表中添加内容, 将会 导致问题。 - Øyvind Bråthen

1

从我的观察中,您正在循环内更新共享集合。这意味着,如果您并行执行循环,将会发生数据竞争,因为多个线程将尝试同时更新非同步集合(假设rList或类似的类型),从而导致不一致的状态。

要正确地并行执行,您需要在代码段内使用锁语句包装它:

object locker = new object();
Parallel.Foreach (s, 
   t => 
   {  
      lock(locker) r.add(method(t));
   });

然而,这将使执行实际上是串行的,因为每个线程都需要获取锁定,而两个线程不能同时获取锁定。

更好的解决方案是为每个线程设置一个本地列表,将部分结果添加到该列表中,然后在所有线程完成时合并结果。可能@Øyvind Knobloch-Bråthen的第二种解决方案是最好的,假设method(t)在这种情况下是真正的CPU瓶颈。


1

修改此问题的正确答案 更改

tasks.Add(Task.Factory.StartNew(method(t);));

//solution will be the following code
tasks.Add(Task.Factory.StartNew(() => { method(t);}));

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