为什么循环中的任务工厂打印超出循环索引?

时间:2015-12-17 07:30:21

标签: c# task-parallel-library

我正在学习在C#中使用任务并行库(TPL),并编写了以下代码(您可以复制它并运行它)。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace parallelTaskLibrary
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 6; i++)
            {
                var t01 = Task.Factory.StartNew(() => Console.WriteLine("in loop: i = {0}", i));
            }

            Console.WriteLine("press any key to terminate...");
            Console.ReadKey();
        }      
    }
}

for循环中,计数器索引i无法启动值为i = 6的迭代。但是,我得到的输出是:

press any key to terminate...
in loop: i = 6
in loop: i = 6
in loop: i = 6
in loop: i = 6
in loop: i = 6
in loop: i = 6

然而,在另一次发布中(代码没有改变),我得到了这个:

in loop: i = 1
in loop: i = 1
in loop: i = 2
in loop: i = 3
in loop: i = 4
in loop: i = 5
press any key to terminate...

这似乎是合理的......

我调试了代码,发现i值为:0,1,3,4,5,6

这是怎么发生的?

为什么我在循环中得到(i = 6)?

我的代码有什么问题吗?

注意:我正在使用visual studio 2010

5 个答案:

答案 0 :(得分:11)

因为存在关闭问题。您需要在临时变量中复制变量i

for (int i = 0; i < 6; i++)
{
    var tempi = i;
    var t01 = Task.Factory.StartNew(() => Console.WriteLine("in loop: i = {0}", tempi));
}

因为您的任务是从另一个线程开始的。但是你不要在当前的线程中等待那个任务。所以程序会回来并增加计数器。由于您创建的委托使用原始计数器,因此它会打印6。因为计数到6比创建新任务要快得多。

使用调试器时,新任务将有机会在继续递增计数器之前打印该值,

答案 1 :(得分:1)

显然,当您的代码(具有Console.WriteLine语句的委托)被执行时,您将打印“i”的值,这可能是稍晚的

这可能是你想要的:

for (int i = 0; i < 6; i++)
{
  var j = i;
  var t01 = Task.Factory.StartNew(() => Console.WriteLine("in loop: i = {0}", j));
}

&gt;我的代码有什么问题吗?

是: - )

答案 2 :(得分:1)

使用该代码,您可以获得有关在编程multi-threaded-applications时必须注意的内容的工作示例。问题是启动thead需要一些时间,在这段时间内,主线程完成迭代for-loop。当主线程完成i == 6并且每个线程访问i的值为6.解决方案是创建局部变量并复制i的值

for(...)
{
    int temp = i;
    var t1 //.....
}

答案 3 :(得分:1)

for (int i = 0; i < 6; i++)

从C#2012开始,以下代码将按预期工作:

foreach (int i in Enumerable.Range(0, 6))

http://ideone.com/naaZNx - 示例。

在循环内声明变量是breaking changes in VS2012之一。

答案 4 :(得分:0)

为什么我在循环中得到(i = 6)?

所有步骤:

i=0
i<6 => create Thread1
i++ => i=1
i<6 => create Thread2
i++ => i=2
i<6 => create Thread3
i++ => i=3
i<6 => create Thread4
i++ => i=4
i<6 => create Thread5
i++ => i=5
i<6 => create Thread6
i++ => i=6
i<6 NO => leave for

主线程执行:

Console.WriteLine("press any key to terminate...");

创建的所有6个线程执行: Console.WriteLine("in loop: i = {0}", i)其中 i = 6