我正在开发一些简单的线程应用程序,但我似乎无法让它工作:
class ThreadTest
{
static Queue<Thread> threadQueue = new Queue<Thread>();
static void Main()
{
//Create and enqueue threads
for (int x = 0; x < 2; x++)
{
threadQueue.Enqueue(new Thread(() => WriteNumber(x)));
}
while(threadQueue.Count != 0)
{
Thread temp = threadQueue.Dequeue();
temp.Start();
}
Console.Read();
}
static void WriteNumber(int number)
{
for (int i = 0; i < 1000; i++)
{
Console.Write(number);
}
}
}
目标基本上是逐个向队列中添加线程,然后逐个遍历队列并弹出一个线程并执行它。因为我的for循环中有“x&lt; 2”,它应该只生成两个线程 - 一个运行WriteNumber(0),另一个运行WriteNumber(1),这意味着我应该以1000 0和我的屏幕上的1000 1是不同的顺序,具体取决于线程的最终执行方式。
我最终得到的是2000 2。我提出的两个可能的解决方案是:我错过了一些明显的东西,或者将变量x发送到WriteNumber函数正在执行pass-by-reference而不是pass-by-value,所以当线程执行它们使用最新版本的x而不是设置函数时的版本。但是,我的理解是,变量在默认情况下在C#中按值传递,并且只有在参数中包含“ref”时才通过引用传递。
答案 0 :(得分:18)
您正在lambda表达式中捕获x
。在启动线程之前,x
的值更改为2。您需要在循环中复制值:
for (int x = 0; x < 2; x++)
{
int copy = x;
threadQueue.Enqueue(new Thread(() => WriteNumber(copy)));
}
Lambda表达式捕获变量。即使传递给WriteNumber
的值是按值传递,但在线程启动之前根本不会调用它 - 此时x
为2。
通过在循环中创建副本,循环的每次迭代都会获得copy
变量的独立“实例”,而 在调用WriteNumber
时,每个copy
变量仍具有x
对该次迭代的相同值。
答案 1 :(得分:9)
这是因为在循环结束后访问了x
的值,到那时它是2
。
您需要使用临时变量来防止变量捕获。
for (int x = 0; x < 2; x++)
{
int tmp = x;
threadQueue.Enqueue(new Thread(() => WriteNumber(tmp)));
}