1

If I create an object which is going to be accessed by two different std::threads, do I need to make any special provisions when I create the object or pass it to the threads?

For example:

class Alpha
{
public:
    int x;
};

void Foo(Alpha* alpha)
{
    while (true)
    {
        alpha->x++;
        std::cout << "Foo: alpha.x = " << alpha->x << std::endl;
    }
}

void Bar(Alpha* alpha)
{
    while (true)
    {
        alpha->x++;
        std::cout << "Bar: alpha.x = " << alpha->x << std::endl;
    }
}

int main(int argc, char * argv[])
{
    Alpha alpha;
    alpha.x = 0;
    std::thread t1(Foo, &alpha);
    std::thread t2(Bar, &alpha);
    t1.join();
    t2.join();

    return 0;
}

This compiles fine, and seems to run fine too. But I haven't explicitly told my program that alpha needs to be accessed by two different threads. Should I be doing this differently?

Karnivaurus
  • 22,823
  • 57
  • 147
  • 247
  • 1
    This is a race condition. See [this](http://stackoverflow.com/questions/34510/what-is-a-race-condition) question for some more info. – phantom May 08 '15 at 14:46
  • Two or more threads can share memory. When threads operate on the variables a race condition can occur. for the simple case of incrementing, should not be a problem. otherwise, could use mutex to lock the resource before operating on it. – 911 May 08 '15 at 14:49
  • 1
    @sam: yes, there is a problem! This is a data race, and therefore undefined behavior. The program is kaputt, as in allowed to set your computer on fire! – Fabio Fracassi May 08 '15 at 15:44
  • @Fabio. you are right. even the simple increment can be problematic. – 911 May 09 '15 at 11:00

2 Answers2

4

You have race condition on alpha.x, as both threads may write when the other read/write its value. You may fix that by changing type of x into std::atomic<int> or by using protecting read/write access by mutex.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
3

If an object is going to be accessed by multiple threads, then you must make provisions for synchronization. In your case, it will suffice to declare the variable x as an atomic:

#include <atomic>

class Alpha
{
public:
    std::atomic<int> x;
};

This will guarantee that any function which increments "x" will actually use the atomic fetch_and_add() method. This guarantees that each thread that increments the variable "x" will get a unique incremented value of x.

In the code you have posted, it is extremely possible that both threads will get a value of 1 for x if the executions interleave just right.

Ken P
  • 552
  • 3
  • 11