如何保证两个类在两个线程中写同一个变量线程安全?
How to ensure two classes in two threads writing same variable thread safe?
我在 2 个线程中同时有两个 classes 运行,比如 class A
和 class B
, A
正在从一些 API 收集数据,并不断将更新的数据推送到 unordered-map
作为 B->m
。同时B
正在使用数据创建自定义classBar
,代码是这样的:
Class A
:
if(m.size() > 0){
//mtx.lock();
lock = B->lock;
if(lock == false){
lock = true;
B->m = m;
lock = false;
}
m.clear();
//mtx.unlock();
}
Class B
:
while(true){
if(m.size() > 0){
//mtx.lock();
if(lock == false){
lock = true;
for (auto it : m) {
std::string symbol = it.first;
std::vector<double> v = it.second;
Bar b;
b.open = v[0];
b.high = v[1];
bars.push_back(b);
}
m.clear();
lock = false;
}
//mtx.unlock();
}}
从这里可以看出,我尝试使用一个名为 lock
的布尔值手动实现互斥锁,当我编译时有时它有时会完成段错误,我认为这是因为边缘情况:当我set lock
to true
in class B
, class A
刚刚跳过 if(lock == false)
and B
is reading数据变化 A
。我的问题是,如何避免这种情况发生?
手动互斥实现将不起作用(至少不是以一种简单的方式)。例如从编译器的角度来看this
lock = strategy->lock;
if(lock == false){
lock = true;
B->m = m;
lock = false;
}
可以改成这样
lock = strategy->lock;
if(lock == false){
B->m = m;
}
因为单线程结果(如果您不另外说明,这是编译器的默认观点)完全相同。
还有其他原因可能会失败,例如部分对象填充。通常是复杂的东西。
所以你必须使用你正在使用的库为你提供的一些同步对象。对于 C++11 及更高版本,您可以简单地使用 std::mutex:
#include <mutex>
std::mutex my_mutex;
...
// some code
{
// lock resource
std::lock_guard<std::mutex> lg(my_mutex);
B->m = m;
}
// automatically releases the lock and continues the execution
您需要 std::mutex 和 std::condition_variable。
std::mutex
用于保护您的代码免受未定义行为的影响,std::condition_variable
用于通知工作线程有工作需要完成。
如果你真的想自己实现一个互斥锁,那么请确保你使用的是原子类型,例如std::atomic_flag
、std::atomic<bool>
等。
为自己实现一个 "thread-safe" 容器是解决您的问题的一个简单选择,它类似于(尚未测试,仅供查看):
#include <mutex>
#include <utility>
#include <unordered_map>
template <class Value, class Key>
class Container {
public:
bool Get(const Key &key, Value &value) {
std::lock_guard<std::mutex> guard(mtx_);
auto ite = mp_.find(key);
if (ite == mp_.end())
return false;
value = std::move(ite->second);
mp_.erase(ite);
return true;
}
template <class ValueType>
void Insert(const Key &key, ValueType &&value) {
std::lock_guard<std::mutex> guard(mtx_);
mp_[key] = std::forward<ValueType>(value);
}
private:
std::unordered_map<Value, Key> mp_;
std::mutex mtx_;
};
实现并发算法需要注意的地方很多,建议先看一些这方面的书(别着急,反正你会很吃力的)。 "C++ concurrency in action" 是一个好的开始。
我在 2 个线程中同时有两个 classes 运行,比如 class A
和 class B
, A
正在从一些 API 收集数据,并不断将更新的数据推送到 unordered-map
作为 B->m
。同时B
正在使用数据创建自定义classBar
,代码是这样的:
Class A
:
if(m.size() > 0){
//mtx.lock();
lock = B->lock;
if(lock == false){
lock = true;
B->m = m;
lock = false;
}
m.clear();
//mtx.unlock();
}
Class B
:
while(true){
if(m.size() > 0){
//mtx.lock();
if(lock == false){
lock = true;
for (auto it : m) {
std::string symbol = it.first;
std::vector<double> v = it.second;
Bar b;
b.open = v[0];
b.high = v[1];
bars.push_back(b);
}
m.clear();
lock = false;
}
//mtx.unlock();
}}
从这里可以看出,我尝试使用一个名为 lock
的布尔值手动实现互斥锁,当我编译时有时它有时会完成段错误,我认为这是因为边缘情况:当我set lock
to true
in class B
, class A
刚刚跳过 if(lock == false)
and B
is reading数据变化 A
。我的问题是,如何避免这种情况发生?
手动互斥实现将不起作用(至少不是以一种简单的方式)。例如从编译器的角度来看this
lock = strategy->lock;
if(lock == false){
lock = true;
B->m = m;
lock = false;
}
可以改成这样
lock = strategy->lock;
if(lock == false){
B->m = m;
}
因为单线程结果(如果您不另外说明,这是编译器的默认观点)完全相同。
还有其他原因可能会失败,例如部分对象填充。通常是复杂的东西。
所以你必须使用你正在使用的库为你提供的一些同步对象。对于 C++11 及更高版本,您可以简单地使用 std::mutex:
#include <mutex>
std::mutex my_mutex;
...
// some code
{
// lock resource
std::lock_guard<std::mutex> lg(my_mutex);
B->m = m;
}
// automatically releases the lock and continues the execution
您需要 std::mutex 和 std::condition_variable。
std::mutex
用于保护您的代码免受未定义行为的影响,std::condition_variable
用于通知工作线程有工作需要完成。
如果你真的想自己实现一个互斥锁,那么请确保你使用的是原子类型,例如std::atomic_flag
、std::atomic<bool>
等。
为自己实现一个 "thread-safe" 容器是解决您的问题的一个简单选择,它类似于(尚未测试,仅供查看):
#include <mutex>
#include <utility>
#include <unordered_map>
template <class Value, class Key>
class Container {
public:
bool Get(const Key &key, Value &value) {
std::lock_guard<std::mutex> guard(mtx_);
auto ite = mp_.find(key);
if (ite == mp_.end())
return false;
value = std::move(ite->second);
mp_.erase(ite);
return true;
}
template <class ValueType>
void Insert(const Key &key, ValueType &&value) {
std::lock_guard<std::mutex> guard(mtx_);
mp_[key] = std::forward<ValueType>(value);
}
private:
std::unordered_map<Value, Key> mp_;
std::mutex mtx_;
};
实现并发算法需要注意的地方很多,建议先看一些这方面的书(别着急,反正你会很吃力的)。 "C++ concurrency in action" 是一个好的开始。