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 называется активным ).
  • Если при вызове возникает исключение, оно передается вызывающей стороне std::call_once , и flag не переключается, так что будет предпринята следующая попытка вызова (такой вызов std::call_once называется исключительным ).
  • Если вызов завершается нормально (такой вызов std::call_once называется возвращающим ), flag переключается, и все остальные вызовы std::call_once с тем же flag гарантированно являются пассивными .

Все активные вызовы на одном и том же flag образуют единый полный порядок, состоящий из нуля или более исключительных вызовов, за которыми следует один возвращающий вызов. Конец каждого активного вызова синхронизируется-с началом следующего активного вызова в этом порядке.

Возврат из возвращающего вызова синхронизируется с возвратами из всех пассивных вызовов на том же flag : это означает, что все параллельные вызовы std::call_once гарантированно наблюдают любые побочные эффекты, созданные активным вызовом, без дополнительной синхронизации.

Содержание

Параметры

flag - объект, для которого выполняется ровно одна функция
f - Callable объект для вызова
args... - аргументы для передачи в функцию

Возвращаемое значение

(нет)

Исключения

  • std::system_error если любое условие препятствует выполнению вызовов std::call_once в соответствии со спецификацией.
  • Любое исключение, выброшенное 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++.

DR Applied to Behavior as published Correct behavior
LWG 2080 C++11 std::invalid_argument would be thrown if f is invalid,
but the scenario where f is invalidated is not specified
removed this error condition
LWG 2442 C++11 the arguments were copied and/or moved before invocation no copying/moving is performed

Смотрите также

(C++11)
вспомогательный объект для гарантии того, что call_once вызывает функцию только один раз
(класс)