帮助理解C#和多线程

时间:2011-02-12 10:00:39

标签: c# multithreading

您好 我正在阅读“C#中的线程化”教程。它提到的一件事是:

“CLR为每个线程分配自己的内存堆栈,以便局部变量保持独立”

还有这个例子:

namespace ConsoleApplication1 {
class Program {
    static void Main(string[] args) {
        for (int i = 0; i < 20; i++) {
            Thread t = new Thread(() => {
                Console.WriteLine(i);
            });
            t.Start();
        }
        Console.ReadLine();
    }
}

}

输出: 1 2 2 4 6 8 10 10 10 10 12 12 14 15 17 18 18 20 20

所以我理解这里发生的事情的方式是:

  • 主线程开始执行for循环。
  • 实例化并定义新线程,使其接收“i”的值 并将其打印到控制台。
  • 同时主线程继续工作,线程实例启动。

作为“i”整数,我的猜测是新线程在其内存堆栈中将拥有自己的副本。然后将值打印到控制台。但结果显示,它的跳跃值从10跳到12或12到14。 那么新线程是否正在接收对i的引用?但是如果“i”是一个整数,那么新线程不应该在其内存堆栈中存储一个新值,而不是似乎是对i的引用。

为什么还有重复值?它的打印次数分别是2,10,12,18,20。

感谢。

3 个答案:

答案 0 :(得分:5)

该示例存在致命缺陷...因为每个线程实际共享一个i变量。它是由lambda表达式捕获

这是一个非常常见的问题,但在线程教程中看到它真的很遗憾。 (我希望这不是我的一篇文章!请告诉我们你在哪里读这篇文章。)Eric Lippert在他的博客文章中非常仔细地写了这篇文章,“关闭了认为有害的循环变量” - part 1; part 2

值得区分线程行为和lambda表达式。真正的线程有自己的堆栈和它们自己的局部变量 - 但是在这里,i由于lambda表达式而在所有线程之间共享。它不是“正常”意义上的局部变量。

这是一个示例,显示每个线程都有自己的局部变量:

using System;
using System.Threading;

public class Test
{
    static void Main()
    {
        for (int x = 0; x < 10; x++)
        {
            new Thread(Count).Start();
        }
    }

    static void Count()
    {
        int threadId = Thread.CurrentThread.ManagedThreadId;
        Console.WriteLine("Thread {0} starting", threadId);
        for (int i = 0; i < 5; i++)
        {
            Console.WriteLine("{0}: {1}", threadId, i);
        }
        Console.WriteLine("Thread {0} ending", threadId);
    }
}

每个线程肯定打印0..4以及自己的线程ID。 <{1}}变量这次是每个线程的真正本地 - 没有共享。

答案 1 :(得分:2)

当一个变量用在lambda表达式中时,就像你的变量i一样,它会被提升到一个闭包中(在你的情况下是main方法)(首先在google上点击“关闭c#”发生是Jon Skeet's article on the subject)。因此,它不是局部变量,不会存在于线程的堆栈中。

答案 2 :(得分:-1)

问题很简单,因为胎面需要时间来初始化和运行,i的值会在平均时间内发生变化。并且在其他线程获得处理器周期进行处理时,还有可能完成多个循环。因此,一次打印多次。