std :: atomic_bool用于取消标志:是std :: memory_order_relaxed正确的内存顺序?

Pau*_*ger 6 c++ atomic memory-barriers cancellation relaxed-atomics

我有一个从套接字读取并生成数据的线程.每次操作后,线程都会检查一个std::atomic_bool标志,看它是否必须提前退出.

为了取消操作,我将取消标志设置为true,然后调用join()工作线程对象.

线程代码和取消函数看起来像这样:

std::thread work_thread;
std::atomic_bool cancel_requested{false};

void thread_func()
{
   while(! cancel_requested.load(std::memory_order_relaxed))
      process_next_element();

}

void cancel()
{
    cancel_requested.store(true, std::memory_order_relaxed);
    work_thread.join();
}
Run Code Online (Sandbox Code Playgroud)

std::memory_order_relaxed此,将原子变量的正确的内存顺序?

rus*_*tyx 5

只要cancel_requestedflag 和其他任何东西之间没有依赖关系,你就应该是安全的。

所示代码看起来不错,假设cancel_requested仅用于加速关闭,但也有有序关闭的规定,例如队列中的哨兵条目(当然,队列本身是同步的)。

这意味着您的代码实际上是这样的:

std::thread work_thread;
std::atomic_bool cancel_requested{false};
std::mutex work_queue_mutex;
std::condition_variable work_queue_filled_cond;
std::queue work_queue;

void thread_func()
{
    while(! cancel_requested.load(std::memory_order_relaxed))
    {
        std::unique_lock<std::mutex> lock(work_queue_mutex);
        work_queue_filled_cond.wait(lock, []{ return !work_queue.empty(); });
        auto element = work_queue.front();
        work_queue.pop();
        lock.unlock();
        if (element == exit_sentinel)
            break;
        process_next_element(element);
    }
}

void cancel()
{
    std::unique_lock<std::mutex> lock(work_queue_mutex);
    work_queue.push_back(exit_sentinel);
    work_queue_filled_cond.notify_one();
    lock.unlock();
    cancel_requested.store(true, std::memory_order_relaxed);
    work_thread.join();
}
Run Code Online (Sandbox Code Playgroud)

如果我们到了那么远,那么cancel_requested也可以成为一个常规变量,代码甚至会变得更简单。

std::thread work_thread;
bool cancel_requested = false;
std::mutex work_queue_mutex;
std::condition_variable work_queue_filled_cond;
std::queue work_queue;

void thread_func()
{
    while(true)
    {
        std::unique_lock<std::mutex> lock(work_queue_mutex);
        work_queue_filled_cond.wait(lock, []{ return cancel_requested || !work_queue.empty(); });
        if (cancel_requested)
            break;
        auto element = work_queue.front();
        work_queue.pop();
        lock.unlock();
        process_next_element(element);
    }
}

void cancel()
{
    std::unique_lock<std::mutex> lock(work_queue_mutex);
    cancel_requested = true;
    work_queue_filled_cond.notify_one();
    lock.unlock();
    work_thread.join();
}
Run Code Online (Sandbox Code Playgroud)

memory_order_relaxed通常很难推理,因为它模糊了顺序执行代码的一般概念。因此,正如赫伯在他的原子武器演讲中所解释的那样,它的用处非常非常有限。

Notestd::thread::join()本身充当两个线程之间的内存屏障。