std::call_once 是阻塞调用吗?

Ziv*_*ivS 5 c++ multithreading std c++11

std::call_once在代码中仅使用一次来初始化一些共享变量。调用代码位于由多个线程触发的回调内。我有兴趣知道,因为我在文档中找不到它,所以std::call_once本质上是否是阻塞的,就好像有一个std::lock_guard相反?实际情况看起来确实如此。

例如,以下内容将在调用"Done"之前打印:print()

#include <future>
#include <iostream>
#include <thread>
#include <mutex>

std::once_flag flag;

void print()
{
    for(int i=0;i<10;i++)
    {
          std::cout << "Hi, my name is " << std::this_thread::get_id() 
            << ", what?" << std::endl;
    }
}

void do_once()
{
    std::cout << "sleeping for a while..." << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(500));
    std::cout << "Done" << std::endl;
}

void work()
{
    std::call_once(flag, [](){ do_once(); });
    print();
}


int main()
{
    auto handle1 = std::async(std::launch::async, work);
    auto handle2 = std::async(std::launch::async, work);
    auto handle3 = std::async(std::launch::async, work);
    auto handle4 = std::async(std::launch::async, work);

    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
Run Code Online (Sandbox Code Playgroud)

我假设确实如此(因为我不知道如何以其他方式实现它),但是这种行为是否得到保证,或者是否有一个编译器决定确实会std::call_once被调用一次但允许其他线程继续并忽略这个电话?

Nat*_*ica 5

是的,这std::call_once是一个阻塞呼叫。从 [thread.once.callonce] 我们有

\n\n
\n

效果:执行 call_once 而不调用其func是被动执行。调用它的 call_once 的执行func是活动执行。主动执行应调用INVOKE (DECAY_COPY ( std::forward<Callable>(func)), DECAY_COPY (std::forward<Args>(args))...). 如果这样的 func 调用抛出异常,则执行异常,否则返回。异常执行应将异常传播给 call_once 的调用者。call_once对于任何给定的的所有执行中once_flag: 至多一个应为返回执行;如果有返回执行,则它应是最后一个活动执行;仅当有返回执行时才会有被动执行。[注意:被动执行允许其他线程可靠地观察较早返回的执行产生的结果。\xe2\x80\x94结束注]

\n\n

同步:对于任何给定的once_flag:所有活动执行都按总顺序发生;活动执行的完成与 (1.10) 总顺序中下一个执行的开始同步;并且返回的执行与所有被动执行的返回同步。

\n
\n\n

强调我的

\n\n

这意味着所有对 的调用都call_once将等到传递给 的函数call_once完成。在您的情况下,这意味着do_once()必须在任何线程调用之前调用print()

\n