简体   繁体   中英

Race condition on stack

I have simple class Hello and I am trying to call member function say_hello on different thread. I created two different implementation it hellos_in_stack and hellos_in_heap . hellos_in_heap works as expected however hellos_on_stack have a race condition on member variable _i . How can I avoid it on stack using mutex ?

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

std::mutex mu;

class Hello
{
  int _i;
public:
  Hello()
  {
      std::lock_guard<std::mutex> lock(mu);
      _i = 0;
  }
  ~Hello(){
  }
  void say_hello()
  {
      std::lock_guard<std::mutex> lock(mu);
      std::cout << "say_hello from thread " << ++_i << " " <<this << " " << std::this_thread::get_id() << std::endl;
  }
};

void hellos_in_stack()
{
    std::vector<std::thread> threads;
    for(int i = 0; i < 4; ++i)
    {
        Hello h;
        threads.push_back(std::thread(&Hello::say_hello, &h));
    }

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


void hellos_in_heap()
{
    std::vector<std::thread> threads;
    std::vector<Hello *> hellos;
    Hello *h = nullptr;
    for(int i = 0; i < 4; ++i)
    {
        h = new Hello();
        hellos.push_back(h);
        threads.push_back(std::thread(&Hello::say_hello, h));
    }

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

    for(auto hello : hellos){
        delete hello;
    }
}

int main()
{
    hellos_in_stack();
    hellos_in_heap();
    return 0;
}

Let's describe the race condition first...

The line Hello h; is constructing h on the main thread's stack. Once the for loop moves on to create the next thread, h is destroyed and another Hello is created -- likely, but not guaranteed, to be at the same address as the previous h .

h must be kept alive for the lifetime of the thread that is running its say_hello method.

One solution would be to create h on the new thread's stack. This can be done like so:

std::vector<std::thread> threads;
for (int i = 0; i < 4; ++i)
{
    threads.emplace_back([]() {
        Hello h;
        h.say_hello();
    });
}

Another option, if you still need the instances of h to be accessible from the main thread, would be to store them in a container.

std::vector<std::thread> threads;
std::list<Hello> hellos;
for (int i = 0; i < 4; ++i)
{
    hellos.emplace_back();
    threads.emplace_back(&Hello::say_hello, &hellos.back());
}

Using a container we've introduced some more complexity. Now, care must be taken to make sure that we use the container itself in a safe way. In this case std::list is used instead of std::vector because calling emplace_back / push_back on std::vector can cause it to resize its buffer. This would destroy Hello instance out from under running threads!


Running example: https://ideone.com/F7STsf

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