有人问了类似的问题here ,但答案通常似乎都与 lambda 表示法有关。我在没有 lambda 的情况下得到了类似的结果,所以我想我会要求澄清一下:
假设我有这样的东西:
for (int i = 0; i < 5; i++)
(new Thread(new ThreadStart(delegate()
{
Console.WriteLine("Thread " + i);
}))).Start();
人们会期望以下输出:
Thread 0
Thread 1
Thread 2
Thread 3
Thread 4
现在我意识到线程没有以任何特定顺序启动,所以让我们假设以上几行可以以任何顺序出现。
但事实并非如此。 相反会发生什么:
Thread 3
Thread 4
Thread 4
Thread 4
Thread 4
或类似的东西,这让我相信它不是传递值 if i,而是传递引用。 (这很奇怪,因为 int 是一种值类型)。
做这样的事情:
for (int i = 0; i < 5; i++)
(new Thread(new ThreadStart(delegate()
{
int j = i;
Console.WriteLine("Thread " + j);
}))).Start();
也无济于事,即使我们复制了 i.我假设原因是它没有及时复制 i。
做这样的事情:
for (int i = 0; i < 5; i++)
{
(new Thread(new ThreadStart(delegate()
{
Console.WriteLine("Thread " + i);
}))).Start();
Thread.Sleep(50);
}
似乎解决了这个问题,但是这是非常不可取的,因为我们在每次迭代中都浪费了 50 毫秒,更不用说如果计算机负载很重,那么 50 毫秒可能还不够。
这是我当前遇到的具体问题的示例:
Thread t = new Thread(new ThreadStart(delgate()
{
threadLogic(param1, param2, param3, param4);
}));
t.Start();
param1 = param2 = param3 = param4 = null;
与:
void threadLogic(object param1, object param2, object param3, object param4)
{
// Do some stuff here...
}
我希望 threadLogic() 在它自己的线程中运行,但是上面的代码给出了空引用异常。我假设这是因为在线程有机会启动之前将值设置为 null。
同样,放置一个 Thread.Sleep(100) 是可行的,但从各个方面来看这都是一个糟糕的解决方案。 你们对这种特殊类型的比赛条件有什么建议?
最佳答案
你需要引入一个临时的:
for (int i = 0; i < 5; i++)
{
int temp = i; // Add this
(new Thread(new ThreadStart(delegate()
{
Console.WriteLine("Thread " + temp);
}))).Start();
}
问题在于委托(delegate)如何关闭外部变量(i
在你的代码中,temp
在我的代码中)。作用域是错误的(在 for 循环之外),所以当线程开始时,i
已经增加了大部分,如果不是全部的话。
对于你的第二个例子,你需要做同样的事情。只做临时工:
var temp1 = param1;
var temp2 = param2;
var temp3 = param3;
var temp4 = param4;
Thread t = new Thread(new ThreadStart(delgate()
{
threadLogic(temp1, temp2, temp3, temp4);
}));
t.Start();
// This is now safe, since the closure above is over "temp*"
param1 = param2 = param3 = param4 = null;
关于c# - 线程启动的竞争条件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4939682/