您好 我正在阅读“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
所以我理解这里发生的事情的方式是:
作为“i”整数,我的猜测是新线程在其内存堆栈中将拥有自己的副本。然后将值打印到控制台。但结果显示,它的跳跃值从10跳到12或12到14。 那么新线程是否正在接收对i的引用?但是如果“i”是一个整数,那么新线程不应该在其内存堆栈中存储一个新值,而不是似乎是对i的引用。
为什么还有重复值?它的打印次数分别是2,10,12,18,20。
感谢。
答案 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的值会在平均时间内发生变化。并且在其他线程获得处理器周期进行处理时,还有可能完成多个循环。因此,一次打印多次。