将非引用值传递给任务

本文关键字:任务 值传 引用 | 更新日期: 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];                        
        });
}