在下面的代码中,正在使用必要的原子来保证所有平台上的无种族语义,或者是否使用了promise.set_value / future.wait暗示某种隐式的内存屏障,它将使我能够依靠标记写对外线程已经可见了?
std::atomic_bool flag{false}; // <- does this need to be atomic?
runInThreadPoolBlocking([&]() {
// do something
flag.store(true);
});
if (flag.load()) // do something
// simplified runInThreadPoolBlocking implementation
template <typename Callable>
void runInThreadPoolBlocking(Callable func)
{
std::promise<void> prom;
auto fut = prom.get_future();
enqueueToThreadPool([&]() {
func();
prom.set_value();
});
fut.get();
}
通常,对于thread.join()或Future之类的标准,该标准是否有任何“隐式”内存屏障?
答案 0 :(得分:3)
thread.join()
和promise.set_value()
/ future.wait()
保证暗示内存障碍。
如果您不希望编译器用其他代码对布尔检查或赋值重新排序,则必须使用atomic_bool
。但是在那种特殊情况下,您不能使用原子bool
。如果您未在其他任何地方使用flag
,则可以保证true
为fut.get()
,因为分配和检查位于同步点的相反侧({{1 }})(强制编译器加载实际的flag
值),并保证函数runInThreadPoolBlocking()
仅在执行lambda之后才能完成。
从cplusplus.com到future::get()
的报价,例如:
数据竞赛
修改了将来的对象。共享状态作为 原子操作(不引起数据争用)。
promise::set_value()
也是如此。除了其他东西
...原子操作(不引起数据争用)...
意味着没有一个冲突的评估发生在另一个评估之前(严格的内存排序)。
所有std::
多线程同步原语和工具也是如此,您希望其中一些操作仅在同步点之前或之后发生(例如std::mutex::lock()
或unlock()
,{{1} }等。
请注意,线程对象本身的任何操作都不会与thread::join()
同步(不同于它所代表的线程内的操作)。
答案 1 :(得分:-2)
std::atomic_bool flag{false}; // <- does this need to be atomic?
是。
通话:
prom.get_future()
返回一个std::future<void>
对象。
为了将来,参考文献指出:
类模板std :: future提供了一种机制来访问 异步操作的结果:
异步操作(通过std :: async,std :: packaged_task或std :: promise创建)可以提供std :: future对象 异步操作的创建者。
然后,异步操作的创建者可以使用多种方法来查询,等待或从中提取值。 std :: future。 如果异步操作具有 尚未提供值。
当异步操作准备好将结果发送给创建者时,它可以通过修改共享状态来实现(例如 std :: promise :: set_value)链接到创建者的std :: future。
请注意,std :: future引用了未与任何其他异步返回对象共享的共享状态(相对于 std :: shared_future)。
您无需在此处存储“返回”值,因此该点有点无语,并且由于没有其他担保人(并且整个想法是线程可能仍在并行运行!),因此需要保留布尔值如果共享,则为原子!