我有以下代码(用c#编写,但很容易翻译成您喜欢的语言......)
class Program
{
static int sharedState = 0;
static void Main(string[] args)
{
Thread t1 = new Thread(UpdateState);
Thread t2 = new Thread(UpdateState);
t1.Start();
t2.Start();
t1.Join();
t2.Join();
Console.WriteLine("sharedState value is {0}.", sharedState);
}
static void UpdateState()
{
for (int i = 0; i < 10; i++)
sharedState++;
}
}
正如你们任何人都可以猜到的,这段代码会创建两个工作线程,将共享状态值增加一次10次。在访问和写入共享值“sharedState”时,代码没有任何同步机制(例如互斥,监视器或任何其他...),主线程正在等待两个工作者完成其工作(Join)。 有人可以向我解释一下这段代码的问题是什么,最后可能是'sharedState'的值是2吗? (我的一个朋友被问到这个问题,他们告诉他这是可能的,我们都不明白......) 顺便说一句 - 当我运行这段代码时,我每次都得20分......
答案 0 :(得分:3)
线程的寿命不够长。如果这么短的循环,它们同时运行的几率很小,尤其是在多核机器上。当你启动第二个线程时,第一个线程已经完成。因为它只需要几分之一微秒,远远少于启动线程所需的时间。让它们循环至少一千万次以增加赔率并观看比赛。发布版本中我的机器上的输出:
sharedState值为13952221。
每次运行时都会显示不同的值。
答案 1 :(得分:2)
它不是线程安全的,因为
sharedState++;
在内部被解释为:
现在,如果此操作不是原子/同步,则可以让更多线程同时从静态属性中获取值,但只会存储从最后一个线程进行的修改。
如果您想要线程安全增量,则应使用Interlocked.Increment
答案 2 :(得分:0)
确实存在问题。但是只有经常运行循环才能实现:
假设状态为1
线程a读取状态并得到1
线程a由操作系统暂停,b运行
线程b读取状态并得到1
线程b计算1 + 1并写入2
线程b读取2
线程b计算2 + 1并写入3
...
线程a计算1 + 1并写入2
执行时3个增量,状态从1变为2
但是你永远不会得到低于最低边界条件的结果。因此,如果你从0开始并且每个线程增加10次,那么无论发生什么,你总是会得到10或更高。
对于snychronisation: Interlocked.Increment(ref whatever)是你的朋友。它是使用汇编程序中的lock-prefix实现的。这是安全递增值的最有效方法。 使用CompareEcxchange可以执行稍微复杂的操作。但锁(某事)也非常有效。
答案 3 :(得分:0)
当然,这在实践中极不可能!