我有以下程序,我生成 1000 个线程来递增共享变量 a,然后让每个线程休眠 1 秒:
using System;
using System.Diagnostics;
using System.Diagnostics.Metrics;
using System.Runtime.CompilerServices;
using System.Threading;
class Program
{
static volatile int a = 0;
static void Main()
{
List<Thread> threads = new List<Thread>();
for (int i = 0; i < 1000; i++)
{
var t = new Thread(() =>
{
a++;
Thread.Sleep(1000);
});
t.Start();
threads.Add(t);
}
// foreach(Thread t in threads) t.Join();
Thread.Sleep(60000);
Console.WriteLine(a);
Console.ReadKey();
}
}
问题:
当我运行包含 Thread.Sleep(1000) 行并注释掉 t.Join() 的代码时,Console.WriteLine(a) 的输出小于 1000,即使我允许线程完成 60 秒.
如果我取消注释 t.Join() 循环或注释掉 Thread.Sleep(1000) 行,则输出始终为 1000。
问题:
当存在 Thread.Sleep(1000) 且 t.Join() 被注释掉时,为什么输出会有所不同?
为什么使用 t.Join() 或删除 Thread.Sleep(1000) 时输出会变得一致(始终为 1000)?
我希望能从线程行为和同步方面解释这里发生的事情。谢谢!
TBH 无法重现
foreach(Thread t in threads) t.Join();
与 Thread.Sleep
效果 - 两者始终(在相对较少的尝试次数下)给出预期总和。我建议将代码更改为:
var t = new Thread(() =>
{
Thread.Sleep(1000);
a++;
});
即改变操作顺序,那么
foreach(Thread t in threads) t.Join();
技巧将停止工作。事实上这只是一个巧合,我猜想与 a++
是相对较快的操作这一事实有关,并且由于您在将线程添加到列表并创建下一个线程之前启动线程,因此它有足够的时间来完成。更改睡眠和增量顺序将主要问题带到前面 - a++
不是原子的,您应该使用一些同步,例如 Interlocked.Increment