简体   繁体   中英

C++ threads race condition simulation

Here is a C++ program that runs 10 times with 5 different threads and each thread increments the value of counter so the final output should be 500 , which is exactly what the program is giving output. But i cant understand why is it giving 500 every time the output should be different as the increment operation is not atomic and there are no locks used so the program should give out different outputs in each case.

edit to increase probability of race condition i increased the loop count but still couldn't see any varying output

#include <iostream>
#include <thread>
#include <vector>

struct Counter {
    int value;

    Counter() : value(0){}

    void increment(){
        value = value + 1000;
    }
};

int main(){
    int n = 50000;
    while(n--){
    Counter counter;

    std::vector<std::thread> threads;
    for(int i = 0; i < 5; ++i){
        threads.push_back(std::thread([&counter](){
            for(int i = 0; i < 1000; ++i){
                counter.increment();
            }
        }));
    }

    for(auto& thread : threads){
        thread.join();
    }

    std::cout << counter.value << std::endl;
    }
    return 0;
}

You're just lucky :)

Compiling with clang++ my output is not always 500:

500
425
470
500
500
500
500
500
432
440

Note

Using g++ with -fsanitize=thread -static-libtsan:

WARNING: ThreadSanitizer: data race (pid=13871)
  Read of size 4 at 0x7ffd1037a9c0 by thread T2:
    #0 Counter::increment() <null> (Test+0x000000509c02)
    #1 main::{lambda()#1}::operator()() const <null> (Test+0x000000507ed1)
    #2 _M_invoke<> /usr/include/c++/5/functional:1531 (Test+0x0000005097d7)
    #3 operator() /usr/include/c++/5/functional:1520 (Test+0x0000005096b2)
    #4 _M_run /usr/include/c++/5/thread:115 (Test+0x0000005095ea)
    #5 <null> <null> (libstdc++.so.6+0x0000000b8c7f)

  Previous write of size 4 at 0x7ffd1037a9c0 by thread T1:
    #0 Counter::increment() <null> (Test+0x000000509c17)
    #1 main::{lambda()#1}::operator()() const <null> (Test+0x000000507ed1)
    #2 _M_invoke<> /usr/include/c++/5/functional:1531 (Test+0x0000005097d7)
    #3 operator() /usr/include/c++/5/functional:1520 (Test+0x0000005096b2)
    #4 _M_run /usr/include/c++/5/thread:115 (Test+0x0000005095ea)
    #5 <null> <null> (libstdc++.so.6+0x0000000b8c7f)

shows the race condition. (Also, on my system the output shows results different than 500).

The options for g++ are explained in the documentage for g++ (eg: man g++). See also: https://github.com/google/sanitizers/wiki#threadsanitizer .

Just because your code has race conditions does not mean they occur. That is the hard part about them. A lot of times they only occur when something else changes and timing is different.

here are several issues: incrementing to 100 can be done really fast. So your threads may be already halfway done before the second one is started. Same for the next thread etc. So you never know you have really 5 in parallel.

You should create a barrier at the beginning of each thread to make sure they start all at the same time.

Also maybe try a bit more than "100" and only 5 threads. But it all depends on the system / load / timing. etc.

to increase probability of race condition i increased the loop count but still couldn't see any varying output

Strictly speaking you have data race in this code which is Undefined Behavior and therefore you cannot reliably reproduce it.

But you can rewrite Counter to some "equivalent" code with artificial delays in increment :

struct Counter {
    int value;

    Counter() : value(0){}

    void increment(){
        int val=value;
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
        ++val;
        value=val;
    }
};

I've got the following output with this counter which is far less than 500 :

100
100
100
100
100
101
100
100
101
100

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM