将非引用值传递给任务
本文关键字:任务 值传 引用 | 更新日期: 2023-09-27 18:31:01
当将 int 变量传递给任务时,我经常感到奇怪的反感,例如在此示例中:
List<List<object>> ListToProcess = new List<List<object>>();
// place some lists in list to process
foreach (var temp in Foo)
ListToProcess.Add(temp);
foreach (var tempArray in ListToProcess)
{
// initialize each list in ListToProcess
}
int numberOfChunks = ListToProcess.Count;
Task[] tasks = new Task[numberOfChunks];
for (int counter = 0; counter < numberOfChunks; counter++)
{
tasks[counter] = Task.Factory.StartNew(() =>
{
// counter is always = 5 why? <---------------------------
var t = ListToProcess[counter];
});
}
如何解决这个问题?
这称为闭包。您不是在使用变量的值,而是在使用变量本身。当代码执行时,它使用执行时的值,而不是定义任务时的值。
要解决此问题,我相信您会执行以下操作:
for (int counter = 0; counter < numberOfChunks; counter++)
{
int cur = counter;
tasks[counter] = Task.Factory.StartNew(() =>
{
// counter is always = 5 why? <---------------------------
var t = ListToProcess[cur];
});
}
不能保证何时访问 StartNew 操作块中的"计数器"变量。可能发生的情况是,所有 5 个值都被循环遍历,并创建了任务,然后计划运行任务。
当它们运行时,将执行以下操作:
var t = ListToProcess[counter];
但在这个阶段,计数已经等于 5。
也许您应该查看并行集合?
ListToProcess.AsParallel().ForAll(list => dosomething(list));
这个区域还有许多其他选择。
for (int counter = 0; counter < numberOfChunks; counter++)
{
var referenceVariable = new{val=counter};
tasks[counter] = Task.Factory.StartNew(() =>
{
var t = ListToProcess[referenceVariable.val];
});
}
由于变量是捕获的,因此您可以通过在每个循环中重新声明一个新变量来解决此问题。
for (int counter = 0; counter < numberOfChunks; counter++)
{
int localCounter = counter;
tasks[localCounter] = Task.Factory.StartNew(() =>
{
// counter is always = 5 why? <---------------------------
var t = ListToProcess[localCounter];
});
}