-1

I would like to understand how exactly the race condition works. Specifically on this example. The result of this program is max value i.e 200 000 or less than this e.g 150 000. So my question is when it stops counting when result is less than 200 000, how it works, and how it looks like step by step. I think if i can understand this on that example it can helps me understand general idea about multithreading. Thanks in advance!

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();
    }
}

mesne12
  • 3
  • 1
  • 1
    For any non-Polish-speakers reading, `Watek` == `Thread`, `Kontekst` == `Context`, `Dzialanie` == `Action`, and `SemaforyPrzyklad` == `SemaphoresExample`. – xander Nov 29 '19 at 00:10

1 Answers1

1

If you haven't already, please read the answer to What is a race condition?.

The problem in your example is in statement kon.x += 1.0. This looks like an atomic operation, but it's not. The += operator (formally called the Addition assignment operator is not thread-safe.

As explained in the answer to Is C# += thread safe?, kon.x += 1.0 is equivalent to:

var temp = kon.x + 1.0;
kon.x = temp;

This creates a "check-then-act" situation. Because two threads are manipulating the context independently, you could end up with a sequence of events like the following:

// 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

Both threads read the same initial value (100) and incremented it, so we effectively "lost" an increment.

In more extreme cases, one thread might work much faster than the other, resulting in multiple lost increments:

// 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
xander
  • 1,689
  • 10
  • 18