首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C# TaskFactory没有完成所有迭代

C# TaskFactory没有完成所有迭代
EN

Stack Overflow用户
提问于 2018-05-08 19:18:42
回答 1查看 84关注 0票数 2

我在使用TaskFactory时遇到了一些奇怪的事情:

代码语言:javascript
复制
Task<int[]> parent = Task.Run(() =>
{
    int length = 100;
    var results = new int[length];
    TaskFactory tf = new TaskFactory(TaskCreationOptions.AttachedToParent,
        TaskContinuationOptions.ExecuteSynchronously);

    // Create a list of tasks that we can wait for
    List<Task> taskList = new List<Task>();
    for (int i = 0; i < length - 1; i++) // have to set -1 on length else out of bounds exception, huh?
    {
        taskList.Add(tf.StartNew(() => results[i] = i));
    }
    // Now wait for all tasks to complete
    Task.WaitAll(taskList.ToArray());

    return results;
});

parent.Wait();

var finalTask = parent.ContinueWith(
     parentTask =>
     {
        foreach (int i in parentTask.Result)
        {
            Console.WriteLine(i);
        }
    });

finalTask.Wait();

Console.ReadKey();

这提供了一个与以下类似的输出:

0 0 0 4 5 0 0 0 10 0 12 13 14 .0 99

我不明白为什么不是所有的指数都不是零。

谢谢,

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-05-08 19:24:46

当您用lambda捕获变量时,这个变量被放置到编译器生成的对象中,这个对象在内部和外部作用域之间共享。当你这样做时:

代码语言:javascript
复制
for (int i = 0; i < length - 1; i++)
{
    taskList.Add(tf.StartNew(() => results[i] = i));
}

变量i在循环和所有子任务之间共享。只有一个i,当任务运行时,它正在被循环修改。这是一种竞争条件,每次都会导致数组中出现看似随机的数据,这取决于任务的调度方式。

解决这一问题的最简单方法是使一个不可变的变量作用于循环主体:

代码语言:javascript
复制
for (int i = 0; i < length; i++) // You can now use length instead of length - 1
{
    int innerI = i;
    taskList.Add(tf.StartNew(() => results[innerI] = innerI));
}

现在每个任务都有一个单独的innerI变量,它被精确地赋值i一次,并且不会改变。

想象一下编译器将旧代码转换为

代码语言:javascript
复制
class Generated1
{
    public int i;
}
var context = new Generated1(); // Exactly one copy of i
for (context.i = 0; context.i < length - 1; context.i++)
{
    taskList.Add(tf.StartNew(() => results[context.i] = context.i));
}

当编译器转换的新代码变成

代码语言:javascript
复制
class Generated2
{
    public int innerI;
}
for (int i = 0; i < length - 1; i++)
{
    var context = new Generated2(); // New copy of innerI for every loop iteration
    context.innerI = i;
    taskList.Add(tf.StartNew(() => results[context.innerI] = context.innerI));
}

关于为什么必须使用length - 1:有些任务直到循环完成后才运行。此时,i == length,所以当您尝试将i用作数组中的索引时,您将得到一个i。使用i修复竞争条件时,这种情况将不再发生。

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50240874

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档