foreach (var t in s)
{
r.add(method(test));
}
但是这只会在前一个语句完成后运行下一个语句。我该如何使它同时运行?
foreach (var t in s)
{
r.add(method(test));
}
但是这只会在前一个语句完成后运行下一个语句。我该如何使它同时运行?
//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();
Task<int>
而不仅仅是 Task
吗?在我的机器上,这段代码可以编译并正常工作。 - Øyvind Bråthen您可以使用Parallel.ForEach
,它将利用多个线程并行执行。您必须确保所有调用的代码都是线程安全的,并且可以在并行环境下执行。
Parallel.ForEach(s, t => r.add(method(t));
从我的观察中,您正在循环内更新共享集合。这意味着,如果您并行执行循环,将会发生数据竞争,因为多个线程将尝试同时更新非同步集合(假设r
是List
或类似的类型),从而导致不一致的状态。
要正确地并行执行,您需要在代码段内使用锁语句包装它:
object locker = new object();
Parallel.Foreach (s,
t =>
{
lock(locker) r.add(method(t));
});
然而,这将使执行实际上是串行的,因为每个线程都需要获取锁定,而两个线程不能同时获取锁定。
更好的解决方案是为每个线程设置一个本地列表,将部分结果添加到该列表中,然后在所有线程完成时合并结果。可能@Øyvind Knobloch-Bråthen的第二种解决方案是最好的,假设method(t)
在这种情况下是真正的CPU瓶颈。
修改此问题的正确答案 更改
tasks.Add(Task.Factory.StartNew(method(t);));
到
//solution will be the following code
tasks.Add(Task.Factory.StartNew(() => { method(t);}));
r
、add
、method
和test
是否线程安全? - Yahia