Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forcing race between threads using C++11 threads

Just got started on multithreading (and multithreading in general) using C++11 threading library, and and wrote small short snipped of code.

 #include <iostream>
 #include <thread>

int x = 5; //variable to be effected by race 

    //This function will be called from a thread
    void call_from_thread1() {
    for (int i = 0; i < 5; i++) { 
           x++;
          std::cout << "In Thread 1 :" << x << std::endl;
        }
    }    

    int main() {
        //Launch a thread
        std::thread t1(call_from_thread1);

       for (int j = 0; j < 5; j++) {
            x--;
            std::cout << "In Thread 0 :" << x << std::endl;
        }

        //Join the thread with the main thread
        t1.join();

    std::cout << x << std::endl;
    return 0;
    }

Was expecting to get different results every time (or nearly every time) I ran this program, due to race between two threads. However, output is always: 0, i.e. two threads run as if they ran sequentially. Why am I getting same results and is there any ways to simulate or force race between two threads ?

like image 935
newprint Avatar asked Dec 20 '22 23:12

newprint


1 Answers

Your sample size is rather small, and somewhat self-stalls on the continuous stdout flushes. In short, you need a bigger hammer.

If you want to see a real race condition in action, consider the following. I purposely added an atomic and non-atomic counter, sending both to the threads of the sample. Some test-run results are posted after the code:

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

void racer(std::atomic_int& cnt, int& val)
{
    for (int i=0;i<1000000; ++i)
    {
        ++val;
        ++cnt;
    }
}

int main(int argc, char *argv[])
{
    unsigned int N = std::thread::hardware_concurrency();
    std::atomic_int cnt = ATOMIC_VAR_INIT(0);
    int val = 0;

    std::vector<std::thread> thrds;
    std::generate_n(std::back_inserter(thrds), N,
        [&cnt,&val](){ return std::thread(racer, std::ref(cnt), std::ref(val));});

    std::for_each(thrds.begin(), thrds.end(),
        [](std::thread& thrd){ thrd.join();});

    std::cout << "cnt = " << cnt << std::endl;
    std::cout << "val = " << val << std::endl;
    return 0;
}

Some sample runs from the above code:

cnt = 4000000
val = 1871016

cnt = 4000000
val = 1914659

cnt = 4000000
val = 2197354

Note that the atomic counter is accurate (I'm running on a duo-core i7 macbook air laptop with hyper threading, so 4x threads, thus 4-million). The same cannot be said for the non-atomic counter.

like image 133
WhozCraig Avatar answered Jan 03 '23 13:01

WhozCraig