堆栈上的竞争条件
Race condition on stack
我有简单的 class Hello
,我正在尝试在不同的线程上调用成员函数 say_hello
。我创建了两个不同的实现 hellos_in_stack
和 hellos_in_heap
。 hellos_in_heap
按预期工作,但是 hellos_on_stack
在成员变量 _i
上存在竞争条件。如何使用 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;
}
先描述一下竞争条件...
行 Hello h;
正在主线程的堆栈上构造 h
。一旦 for
循环继续创建下一个线程,h
将被销毁并创建另一个 Hello
- 可能但不能保证与前一个 h
.
h
必须在 运行 其 say_hello
方法的线程的生命周期内保持活动状态。
一种解决方案是在新线程的堆栈上创建 h
。可以这样做:
std::vector<std::thread> threads;
for (int i = 0; i < 4; ++i)
{
threads.emplace_back([]() {
Hello h;
h.say_hello();
});
}
如果您仍然需要从主线程访问 h
的实例,另一种选择是将它们存储在容器中。
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());
}
我们使用容器引入了更多的复杂性。现在,必须注意确保我们以安全的方式使用容器本身。在这种情况下,使用 std::list
而不是 std::vector
,因为在 std::vector
上调用 emplace_back
/push_back
会导致它调整缓冲区大小。这会破坏 运行 个线程下的 Hello
个实例!
我有简单的 class Hello
,我正在尝试在不同的线程上调用成员函数 say_hello
。我创建了两个不同的实现 hellos_in_stack
和 hellos_in_heap
。 hellos_in_heap
按预期工作,但是 hellos_on_stack
在成员变量 _i
上存在竞争条件。如何使用 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;
}
先描述一下竞争条件...
行 Hello h;
正在主线程的堆栈上构造 h
。一旦 for
循环继续创建下一个线程,h
将被销毁并创建另一个 Hello
- 可能但不能保证与前一个 h
.
h
必须在 运行 其 say_hello
方法的线程的生命周期内保持活动状态。
一种解决方案是在新线程的堆栈上创建 h
。可以这样做:
std::vector<std::thread> threads;
for (int i = 0; i < 4; ++i)
{
threads.emplace_back([]() {
Hello h;
h.say_hello();
});
}
如果您仍然需要从主线程访问 h
的实例,另一种选择是将它们存储在容器中。
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());
}
我们使用容器引入了更多的复杂性。现在,必须注意确保我们以安全的方式使用容器本身。在这种情况下,使用 std::list
而不是 std::vector
,因为在 std::vector
上调用 emplace_back
/push_back
会导致它调整缓冲区大小。这会破坏 运行 个线程下的 Hello
个实例!