【发布时间】:2021-12-14 10:13:47
【问题描述】:
在 Windows 10 上运行的以下 .Net 5 多线程代码应该不确定地计算 _sum,例如 2,4 和 8。但是,_sum 始终为 10,即正确答案。好像已经在_sum 上应用了锁。
谁能解释一下这个问题?
using System;
using System.Threading;
namespace MtAdd
{
class Program
{
private static int _sum = 0; //shared resource
static void Main(string[] args)
{
var threads = new Thread[10];
//Add 1 to _sum for ten times.
for (var i = 0; i < threads.Length; i++)
{
threads[i] = new Thread(AddOne);
threads[i].Start();
}
foreach (var t in threads)
{
t.Join();
}
Console.WriteLine("[{0}] sum = {1}",
Thread.CurrentThread.ManagedThreadId,
_sum);
}
private static void AddOne()
{
Console.WriteLine("[{0}] AddOne called", Thread.CurrentThread.ManagedThreadId);
_sum++; //critical section
}
}
}
[4] AddOne called
[5] AddOne called
[7] AddOne called
[6] AddOne called
[8] AddOne called
[9] AddOne called
[10] AddOne called
[11] AddOne called
[12] AddOne called
[13] AddOne called
[1] sum = 10
【问题讨论】:
-
Protip:一般来说你应该从不需要直接使用
Thread。如果您认为需要使用Thread,那么您可能不需要。 -
线程竞赛错误依赖于时机来做他们的坏事。在这种情况下,两个线程必须在完全相同的时间读取 sum 的值,然后再递增它以演示竞争。这肯定会发生,但是您使用 Console.WriteLine() 使其不太可能发生。这需要一个内部锁来确保两个线程不能同时写入控制台。从而确保这些线程不太可能同时到达 sum++。现在机器变得很忙,不小心延迟了一个线程。去掉Console.WriteLine(),加起来一百万增加几率。
-
除了@HansPassant 评论之外,线程内完成的事情也非常小,一个线程可以在开始返回之前完成,下一个线程被创建并被启动。可能在线程中使用信号量等待并在所有线程启动并运行时释放它以增加可能性。
-
如果你将
AddOne()的实现改为循环,你会发现它出错了:for (int i = 0; i < 100000; ++i) _sum++; -
@Llama 啊,是的,对不起,现在是凌晨 3:30……我该走了
标签: c# .net windows multithreading