竞争条件究竟是如何工作的?逐步解释给定的例子
How exactly does the race condition work? Explaining step by step on the given example
我想了解竞争条件究竟是如何运作的。具体在这个例子上。该程序的结果是最大值,即 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();
}
}
如果您还没有,请阅读 What is a race condition? 的答案。
您的示例中的问题在语句 kon.x += 1.0
中。这看起来 , but it's not. The +=
operator (formally called the Addition assignment operator 不是线程安全的。
如对 Is C# += thread safe? 的回答中所述,kon.x += 1.0
等同于:
var temp = kon.x + 1.0;
kon.x = temp;
这造成了 "check-then-act" 的情况。因为两个线程独立地操作上下文,所以您最终可能会得到如下所示的一系列事件:
// 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) 并递增它,因此我们有效地 "lost" 递增。
在更极端的情况下,一个线程可能比另一个线程工作得更快,从而导致多次丢失增量:
// 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
我想了解竞争条件究竟是如何运作的。具体在这个例子上。该程序的结果是最大值,即 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();
}
}
如果您还没有,请阅读 What is a race condition? 的答案。
您的示例中的问题在语句 kon.x += 1.0
中。这看起来 +=
operator (formally called the Addition assignment operator 不是线程安全的。
如对 Is C# += thread safe? 的回答中所述,kon.x += 1.0
等同于:
var temp = kon.x + 1.0;
kon.x = temp;
这造成了 "check-then-act" 的情况。因为两个线程独立地操作上下文,所以您最终可能会得到如下所示的一系列事件:
// 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) 并递增它,因此我们有效地 "lost" 递增。
在更极端的情况下,一个线程可能比另一个线程工作得更快,从而导致多次丢失增量:
// 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