我想了解竞争条件的确切工作原理。具体到这个例子。该程序的结果是最大值,即 200 000 或小于此值,例如 150 000。所以我的问题是,当结果小于 200 000 时,它何时停止计数,它是如何工作的,以及它是如何一步一步地看起来的。我想如果我能在那个例子上理解这一点,它可以帮助我理解关于多线程的一般概念。提前感谢!
using System;
using System.Threading;
class Kontekst
{
public double x = 0.0;
};
class Watek
{
public Kontekst kon;
public int num;
public Watek(Kontekst kon_, int num_)
{
kon = kon_;
num = num_;
}
public void Dzialanie()
{
Console.WriteLine("Watek " + num);
for (int i = 0; i < 100000; ++i) kon.x += 1.0;
}
};
public class SemaforyPrzyklad
{
public static void Main(string[] args)
{
Kontekst kon = new Kontekst();
Watek w1 = new Watek(kon, 1);
Watek w2 = new Watek(kon, 2);
Thread watek1 = new Thread(w1.Dzialanie);
Thread watek2 = new Thread(w2.Dzialanie);
watek1.Start();
watek2.Start();
watek1.Join();
watek2.Join();
Console.WriteLine("x = " + kon.x);
Console.ReadKey();
}
}
如果您还没有,请阅读什么是竞争条件?的答案。
示例中的问题出在语句kon.x += 1.0
中。这看起来像一个原子操作,但事实并非如此。+=
运算符(正式称为 Addition 赋值运算符(不是线程安全的。
如回答 C# += 线程安全吗?中所述,kon.x += 1.0
等效于:
var temp = kon.x + 1.0;
kon.x = temp;
这就造成了"先检查后行动"的情况。由于两个线程独立地操作上下文,因此最终可能会得到如下所示的事件序列:
// let kon.x = 100
[w1] var temp = kon.x + 1.0; // w1.temp = 101
[w2] var temp = kon.x + 1.0; // w2.temp = 101
[w1] kon.x = temp; // kon.x = 101
[w2] kon.x = temp; // kon.x = 101
两个线程读取相同的初始值 (100( 并递增它,因此我们实际上"丢失"了一个增量。
在更极端的情况下,一个线程的运行速度可能比另一个线程快得多,从而导致多个增量丢失:
// let kon.x = 100
[w1] var temp = kon.x + 1.0; // w1.temp = 101
[w2] var temp = kon.x + 1.0; // w2.temp = 101
// for some reason, w2 sleeps while w1 completes several iterations
[w1] kon.x = temp; // kon.x = 101
[w1] var temp = kon.x + 1.0; // w1.temp = 102
[w1] kon.x = temp; // kon.x = 102
[w1] var temp = kon.x + 1.0; // w1.temp = 103
[w1] kon.x = temp; // kon.x = 103
// w2 wakes up from its sleep and writes a very old value to kon.x
[w2] kon.x = temp; // kon.x = 101