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.