Namespaces
Variants

std:: call_once

From cppreference.net
Concurrency support library
Threads
(C++11)
(C++20)
this_thread namespace
(C++11)
(C++11)
Cooperative cancellation
Mutual exclusion
Generic lock management
(C++11)
(C++11)
(C++11)
call_once
(C++11)
Condition variables
(C++11)
Semaphores
Latches and Barriers
(C++20)
(C++20)
Futures
(C++11)
(C++11)
(C++11)
Safe reclamation
Hazard pointers
Atomic types
(C++11)
(C++20)
Initialization of atomic types
(C++11) (deprecated in C++20)
(C++11) (deprecated in C++20)
Memory ordering
(C++11) (deprecated in C++26)
Free functions for atomic operations
Free functions for atomic flags
定义于头文件 <mutex>
template < class Callable, class ... Args >
void call_once ( std:: once_flag & flag, Callable && f, Args && ... args ) ;
(C++11 起)

即使从多个线程并发调用,也确保 Callable 对象 f 仅被执行一次。

详细说明:

  • 如果在调用 std::call_once 时, flag 已表明 f 已被调用过,则 std::call_once 立即返回(这样的 std::call_once 调用被称为 被动 调用)。
  • 否则, std::call_once 调用 INVOKE ( std:: forward < Callable > ( f ) , std:: forward < Args > ( args ) ... ) 。与 std::thread 构造函数或 std::async 不同,这些参数不会被移动或复制,因为它们不需要传递到另一个执行线程(这样的 std::call_once 调用被称为 active )。
  • 若该调用抛出异常,异常将传播至 std::call_once 的调用方,且 flag 不会被翻转,因此后续调用仍会尝试执行(此类对 std::call_once 的调用称为 异常调用 )。
  • 若该调用正常返回(此类对 std::call_once 的调用称为 返回调用 ),则 flag 被翻转,且所有其他使用相同 flag std::call_once 调用均保证为 被动调用

同一 flag 上的所有 活动 调用构成一个单一全序,该顺序由零个或多个 异常 调用后接一个 返回 调用组成。每个 活动 调用的结束都与该顺序中下一个 活动 调用实现同步。

返回中 调用的返回与同一 flag 上所有 被动 调用的返回实现同步:这意味着所有对 std::call_once 的并发调用都保证能观察到 活动 调用所产生的任何副作用,且无需额外的同步机制。

目录

参数

flag - 一个对象,对应执行且仅执行一个函数
f - Callable 待调用对象
args... - 传递给函数的参数

返回值

(无)

异常

  • 若任何条件阻止对 std::call_once 的调用按规范执行,则抛出 std::system_error
  • f 抛出的任何异常。

注释

如果对 std::call_once 的并发调用传递了不同的函数 f ,具体哪个 f 会被调用是未指定的。被选中的函数将在其对应的 std::call_once 调用所在线程中执行。

函数局部静态变量的初始化保证即使从多个线程调用也只会发生一次,并且可能比使用 std::call_once 的等效代码更高效。

该函数的POSIX等效函数是 pthread_once

示例

#include <iostream>
#include <mutex>
#include <thread>
std::once_flag flag1, flag2;
void simple_do_once()
{
    std::call_once(flag1, [](){ std::cout << "Simple example: called once\n"; });
}
void may_throw_function(bool do_throw)
{
    if (do_throw)
    {
        std::cout << "Throw: call_once will retry\n"; // 此处可能出现多次
        throw std::exception();
    }
    std::cout << "Did not throw, call_once will not attempt again\n"; // 保证仅执行一次
}
void do_once(bool do_throw)
{
    try
    {
        std::call_once(flag2, may_throw_function, do_throw);
    }
    catch (...) {}
}
int main()
{
    std::thread st1(simple_do_once);
    std::thread st2(simple_do_once);
    std::thread st3(simple_do_once);
    std::thread st4(simple_do_once);
    st1.join();
    st2.join();
    st3.join();
    st4.join();
    std::thread t1(do_once, true);
    std::thread t2(do_once, true);
    std::thread t3(do_once, false);
    std::thread t4(do_once, true);
    t1.join();
    t2.join();
    t3.join();
    t4.join();
}

可能的输出:

Simple example: called once
Throw: call_once will retry
Throw: call_once will retry
Throw: call_once will retry
Did not throw, call_once will not attempt again

缺陷报告

以下行为变更缺陷报告被追溯应用于先前发布的C++标准。

缺陷报告 应用于 发布时的行为 正确行为
LWG 2080 C++11 f 无效则会抛出 std::invalid_argument
但未说明 f 失效的具体场景
移除此错误条件
LWG 2442 C++11 调用前会对参数进行复制和/或移动 不执行复制/移动操作

参见

(C++11)
用于确保 call_once 仅调用函数一次的辅助对象
(类)
C 文档 关于 call_once