C++ 以线程安全的方式设置具有回调的类成员
我有一个类,它允许通过一个昂贵的计算来设置一个值,我们可以假设计算是在其他线程中异步进行的。该类还有一个获取当前值的方法,如果需要,可以同步执行该计算:C++ 以线程安全的方式设置具有回调的类成员,c++,multithreading,callback,c++14,C++,Multithreading,Callback,C++14,我有一个类,它允许通过一个昂贵的计算来设置一个值,我们可以假设计算是在其他线程中异步进行的。该类还有一个获取当前值的方法,如果需要,可以同步执行该计算: class Example { public: Example() : x_(0) {} void Set(int x) { SomeOtherFunc(x, callback_, false); } void Finished(int y) { x_ = y; }
class Example {
public:
Example() : x_(0) {}
void Set(int x) {
SomeOtherFunc(x, callback_, false);
}
void Finished(int y) {
x_ = y;
}
const int Get() {
if (!x_) {
SomeOtherFunc(1, callback_, true);
}
return x_;
}
private:
int x_;
std::function<void(int)> callback_ =
std::bind(&Example::Finished, this, std::placeholders::_1);
};
我的问题是:
示例
类是线程安全的吗?如果没有,如何做到这一点?看起来锁会起作用,但可能有点过头了SomeOtherFunc
中的工作非常昂贵,因此我只想调用该函数一次。在类中设置一个标志,该标志在每次调用时都设置为true,并在调用之前进行检查,这似乎是合理的(但它是线程安全的吗?)。问题是,如果我调用Set
然后紧接着调用Get
,我希望Get
返回x的“最终”值。我如何确保这一点?也就是说,如果设置了该标志,如何使Get
以线程安全的方式等待回调完成
你可能想用一个 类模板std::future提供了一种访问异步操作结果的机制: 异步操作(通过std::async、std::packaged_任务或std::promise创建)可以向该异步操作的创建者提供std::future对象。 然后,异步操作的创建者可以使用各种方法来查询、等待或从std::future中提取值。如果异步操作尚未提供值,则这些方法可能会阻塞。 当异步操作准备好向创建者发送结果时,它可以通过修改链接到创建者的std::future的共享状态(例如std::promise::set_值)来实现
您的
示例
类不是线程安全的,因为可以同时修改整数x
,这可能导致未定义的行为(数据竞争)。
此外,由于您在Get()
中有一个竞争条件,您可能会多次执行昂贵的计算,方法是选中x
,然后调用将设置它的函数。您需要一种机制来保证在完全线程安全的情况下,
x
的值将精确计算一次(Set()
和Get()
可以同时调用)
标准库提供了一种机制来精确地解决这个问题,call_once()
,旨在实现一次性事件,同时在线程之间正确地同步共享数据。
您可以这样使用它:
#include <mutex>
class Example {
public:
...
void Set(int x) {
std::call_once(flag, SomeOtherFunc, x, callback_, false);
}
...
const int Get() {
std::call_once(flag, SomeOtherFunc, 1, callback_, true);
return x_;
}
private:
std::once_flag flag;
...
};
#包括
课例{
公众:
...
无效集(int x){
std::调用一次(flag,SomeOtherFunc,x,callback,false);
}
...
常量int Get(){
std::call_once(flag,SomeOtherFunc,1,callback_,true);
返回x;
}
私人:
std::once_标志;
...
};
这也可以处理这样的情况,Get()
在回调处理结果时必须等待
请注意,您不再能够(也不必)在Get()
中检查x
的值,因为这将构成数据竞争(另一个线程可能同时更新x
)
还要注意,直接调用回调函数
Finished()
不是线程安全的。可能最好将其移动到private:
部分。如果当前正在进行调用,那么call\u once将在Get()
中阻塞?这正是我想要的。@SteveD是的,call\u once
保证了这一点。。如果SomeOtherFunc()
正在进行计算,则对Get()
(和Set()
)的所有(并发)调用都将被阻止,无论是由Get()
还是Set()
启动。再次感谢,这正是我所需要的
Example e1, e2;
e1.Set(5);
// "10 2"
std::cout << e1.Get() << " " << e2.Get();
Example e;
e.Set(10); // Takes a while
e.Get(); // Need the value now
#include <mutex>
class Example {
public:
...
void Set(int x) {
std::call_once(flag, SomeOtherFunc, x, callback_, false);
}
...
const int Get() {
std::call_once(flag, SomeOtherFunc, 1, callback_, true);
return x_;
}
private:
std::once_flag flag;
...
};