使用Lambda表达式将数据传递给线程

3
for (int i = 0; i < 10; i++)
  new Thread (() => Console.Write (i)).Start();

正如预料的那样,上述代码的输出是不确定的,因为i变量在整个循环生命周期中引用同一内存位置。因此,每个线程都调用Console.Write方法输出一个值,但该值可能会在运行时发生更改。

然而,

for (int i = 0; i < 10; i++)
{
  int temp = i;
  new Thread (() => Console.Write (temp)).Start();
}

程序输出结果不确定!我以为变量temp只在每个循环迭代中是局部的。因此,每个线程都捕获了不同的内存位置,应该没有问题。


2
输出的非确定性本质是什么? - phoog
1
第二个版本应该可以工作 - Eric Lippert 在这里发表了有关循环变量闭包的博客文章:http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx - Paolo
1
它不应该是“确定性的”,因为“调度的线程顺序总是相同”。也许这就是问题的意思? - Anton Kovalenko
问题类似于 - https://dev59.com/znA75IYBdhLWcg3wrbC_#3168381 - Daniel Kelley
1
@JeffWatkins int是一个原始类型,但因为它被闭包捕获,所以实际上存储在堆对象中。第一个示例在堆上创建一个闭包对象,所有10个线程都使用该对象。第二个示例在堆上为每个线程创建10个不同的闭包对象。请参阅Paolo提供的Eric Lippert博客链接或阅读Honza Brestan的答案进行简要讨论。 - phoog
显示剩余4条评论
4个回答

2

您的程序应该有10个Lambda表达式,每个表达式写入从0到9的一个数字到控制台。然而,不能保证线程按顺序执行。


2
OP的代码中没有换行符,因此所有数字都将在同一行上。 - AgentFire
@AgentFire 哦,我明白了。我看到了“WriteLine”,但实际上只有“Write”。我稍微编辑了一下答案。 - phoog
Console.Write()是线程安全的,因为写入其中的整个字符串永远不会出现字符交错的情况。 - Matthew Watson
@MatthewWatson 你说得对。我描述的输出是基于我最近在工作中遇到的经验,但这可能是由于在不同的线程上多次调用控制台所导致的。我到办公室后会检查一下。 - phoog

2

也会输出不确定的结果!

不,它并不会。我已经检查了你的第一个代码(有重复数字)和第二个代码(没有)。

所以一切都正常。就像应该的那样。


1
这里有证据证明OP是正确的,而他的两段代码都是错误的。
同时也有一个带有证明的解决方案。
需要注意的是,“非确定性”意味着线程接收到了错误的参数。顺序将永远不会保证。
下面的代码检查了OP代码的第二部分,并演示了它按预期工作的情况。
我存储了一对(线程标识,参数),然后打印出来与线程输出进行比较,以证明这些对没有被改变。我还添加了几百毫秒的随机休眠时间,所以for循环索引应该在这些时间点明显改变。
        Dictionary<int, int> hash = new Dictionary<int, int>();
        Random r = new Random(DateTime.Now.Millisecond);
        for (int i = 0; i < 10; i++)
        {
            int temp = i;
            var th = new Thread(() =>
            {
                Thread.Sleep(r.Next(9) * 100);
                Console.WriteLine("{0} {1}", 
                    Thread.CurrentThread.GetHashCode(), temp);
            });

            hash.Add(th.GetHashCode(), temp);

            th.Start();
        }

        Thread.Sleep(1000);
        Console.WriteLine();

        foreach (var kvp in hash)
            Console.WriteLine("{0} {1}", kvp.Key, kvp.Value);

1
第二个代码片段应该是确定性的,每个线程最终都会写入它的“temp”,并且它们所有的“temp”将不同。
然而,它不能保证线程按其创建顺序被调度执行。你会看到所有可能的“temp”,但不一定是按升序排列的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接