Ron*_*nen 4 c++ multithreading std undefined-behavior c++11
我听到了很多相互矛盾的答案,现在我不知道该怎么想。公认的知识是,为了在 C++ 中以线程安全的方式共享内存,需要将 volatile与std::mutex 一起使用。
基于这种理解,我一直在编写这样的代码:
volatile bool ready = false;
std::condition_variable cv;
std::mutex mtx;
std::unique_lock<std::mutex> lckr{ mtx };
cv.wait(lckr, [&ready]() -> bool { return ready; });
Run Code Online (Sandbox Code Playgroud)
但后来我在 CppCon 上看到了 Chandler Carruth 的演讲,他说(作为旁注)在这种情况下不需要 volatile,而且我基本上不应该使用 volatile。
然后我在 Stack Overflow 上看到其他答案说永远不应该使用 volatile,而且它还不够好,而且根本不能保证原子性。
钱德勒·卡鲁斯正确吗?我们都错了吗?
现在我有3个选择:
我想知道 C++14 ISO 标准是否允许我编写如下代码:
#include <condition_variable>
#include <mutex>
#include <iostream>
#include <future>
#include <functional>
struct sync_t
{
std::condition_variable cv;
std::mutex mtx;
bool ready{ false };
};
static void threaded_func(sync_t& sync)
{
std::lock_guard<std::mutex> lckr{ sync.mtx };
sync.ready = true;
std::cout << "Waking up main thread" << std::endl;
sync.cv.notify_one();
}
int main()
{
sync_t sync;
{
std::unique_lock<std::mutex> lckr{ sync.mtx };
sync.ready = false;
std::future<void> thread =
std::async(std::launch::async, threaded_func, std::ref(sync));
std::cout << "Preparing to sleep" << std::endl;
sync.cv.wait(lckr, [&sync]() -> bool { return sync.ready; });
thread.get();
}
std::cout << "Done program execution" << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
当我成功时会发生什么:
volatile bool ready{ false };
Run Code Online (Sandbox Code Playgroud)
当我成功时会发生什么:
std::atomic<bool> ready{ false };
Run Code Online (Sandbox Code Playgroud)
限定符对从不同线程 \xe2\x80\x93 访问对象volatile没有必需的影响,它仅保证编译器不会优化单个线程中修改的副作用。来自 cppreference(粗体强调我的):
\n\n\n
\n- 易失性对象- 类型为易失性限定的对象,或者易失性对象的子对象,或者 const-易失性对象的可变子对象。出于优化目的(即,在\n执行时,易失性访问无法被优化或重新排序,\n这会产生另一个可见的副作用,即在易失性访问之前或\n之后排序。这使得易失性对象\n适合与信号处理程序通信,但不适用于\n其他线程执行,请参阅 std::memory_order)。任何通过非易失性类型的泛左值引用易失性对象的尝试(例如,通过对非易失性类型的引用或指针)都会导致未定义的行为。
\n
为了防止从多个线程访问对象时发生未定义的行为,您应该使用std::atomic对象。再次,来自 cppreference:
\n\nstd::atomic 模板的每个实例化和完全特化都定义了一个原子类型。如果一个线程写入原子对象,而另一个线程从中读取,则行为是明确定义的(有关数据争用的详细信息,请参阅内存模型)。
\n此外,对原子对象的访问可以建立线程间同步并按照 std::memory_order 指定的顺序对非原子内存访问进行排序。
\n
| 归档时间: |
|
| 查看次数: |
623 次 |
| 最近记录: |