为Task.Factory.StartNew()传递迭代器的常量副本

时间:2018-03-25 12:43:49

标签: c# multithreading

在我的程序中,我目前有类似的结构:

bash

它无法正常工作,因为函数for (int i=0; i<MyCollection.Count; i++) { Task.Factory.StartNew(()=> { DoStuffWith(MyCollection[i]); DoEvenMoreStuffWith(MyCollection[i]); }); } DoStuffWith()DoEvenMoreStuffWith()花费更多时间来完成,因此其中一个通常会修改集合的错误元素。

我知道我不能在i++之前int a = i,因为它会一样,真的。

如何确保每个线程都使用自己的索引?

P.S。:抱歉,我试着在这里寻找答案,但我甚至不知道如何正确地进行谷歌搜索。我知道它与引用类型和值类型有关,但我之前从未在Task.Factory周围使用它们。

1 个答案:

答案 0 :(得分:2)

如果你使用标准 for循环,你可以在循环体的闭包内保留变量的本地副本(正如你所建议的那样),这个关闭将由您的延期代码的正文捕获。

所以:

for (int i=0; i<MyCollection.Count; i++)
{
    var a = i;
    Task.Factory.StartNew(()=>
    {
        DoStuffWith(MyCollection[a]);
        DoEvenMoreStuffWith(MyCollection[a]);
    });
}

但是,还有更好的方法。

最近(ish),the c# language was changed to close over the loop variable in foreach loops

所以,除非你坚持使用2012年的工具,否则现在你可以:

foreach(var item in MyCollection)
{
    Task.Factory.StartNew(()=>
    {
        DoStuffWith(item);
        DoEvenMoreStuffWith(item);
    });
}

没有发生这个问题。

顺便说一句,你知道difference between Task.Factory.StartNew and Task.Run吗?你应该这样做。