Espacios de nombres
Variantes
Acciones

std::call_once

De cppreference.com
< cpp‎ | thread
 
 
Biblioteca de apoyo de concurrencia
Hilos
(C++11)
(C++20)
Espacio de nombres this_thread
(C++11)
(C++11)
(C++11)
Cancelación cooperativa
Exclusión mutua
(C++11)
Gestión genérica de bloqueo
(C++11)
(C++11)
(C++11)
call_once
(C++11)
(C++11)
Variables de condición
(C++11)
Semáforos
Pestillos y barreras
(C++20)
(C++20)
Futuros
(C++11)
(C++11)
(C++11)
(C++11)
Recuperación segura
(C++26)
Punteros de riesgo
Tipos atómicos
(C++11)
(C++20)
Inicialización de tipos atómicos
(C++11)(en desuso en C++20)
(C++11)(en desuso en C++20)
Orden de memoria
Funciones independientes para operaciones atómicas
Funciones independientes para indicadores atómicos
 
Definido en el archivo de encabezado <mutex>
template< class Callable, class... Args >
void call_once( std::once_flag& flag, Callable&& f, Args&&... args );
(desde C++11)

Ejecuta el objeto invocable (Callable) f exactamente una vez, incluso si se llama concurrentemente desde varios hilos.

En detalle:

  • Si para el momento en que se llama a call_once, flag indica que f ya se llamó, call_once regresa de inmediato (tal llamada a call_once se conoce como pasiva).
  • De lo contrario, call_once invoca a std::forward<Callable>(f) con los argumentos std::forward<Args>(args)... (como si fuera por std::invoke). A diferencia del constructor de std::thread o std::async, los argumentos no se mueven ni se copian porque no es necesario transferirlos a otro hilo de ejecución (tal llamada a call_once se conoce como activa).
  • Si esa invocación lanza una excepción, se propaga al llamante de call_once, y la bandera no se invierte para que se intente realizar otra llamada (tal llamada a call_once se conoce como excepcional).
  • Si esa invocación retorna/regresa normalmente (tal llamada a call_once se conoce como retornante), la bandera se invierte, y se garantiza que todas las demás llamadas a call_once con la misma bandera son pasivas.

Todas las llamadas activas en la misma bandera, flag, forman un solo orden total que consiste en cero o más llamadas excepcionales, seguidas de una llamada retornante. El final de cada llamada activa se sincroniza-con la siguiente llamada activa en ese orden.

El retorno/regreso de la llamada retornante se sincroniza-con los retornos de todas las llamadas pasivas en la misma bandera, flag: esto significa que se garantiza que todas las llamadas concurrentes a call_once observan cualquier efecto secundario producido por la llamada activa, sin sincronización adicional.

Contenido

[editar] Parámetros

flag - Un objeto para el cual se ejecuta exactamente una función.
f - Callable Objeto a invocar.
args... - Argumentos a pasar a la función.

[editar] Valor de retorno

(Ninguno)

[editar] Excepciones

  • std::system_error Si cualquier condición impide que las llamadas a call_once se ejecuten como se especifica.
  • Cualquier excepción lanzada por f.

[editar] Notas

Si llamadas concurrentes a call_once pasan diferentes funciones f, no se especifica cuál f se llamará. La función seleccionada se ejecuta en el mismo hilo que la invocación call_once a la que se pasó.

Se garantiza que la inicialización de variables locales estáticas en una función ocurre solamente una vez incluso cuando se llama desde varios hilos, y puede ser más eficiente que el código equivalente que usa std::call_once.

El equivalente POSIX de esta función es pthread_once.

[editar] Ejemplo

#include <iostream>
#include <thread>
#include <mutex>
 
std::once_flag flag1, flag2;
 
void una_vez_sencillo()
{
    std::call_once(flag1, [](){ std::cout << "Ejemplo sencillo: llamado una vez\n"; });
}
 
void funcion_que_puede_lanzar(bool lanzar)
{
  if (lanzar) {
    // esto puede aparecer más de una vez
    std::cout << "lanzar: call_once lo intentará de nuevo\n";
    throw std::exception();
  }
  // garantizado una vez
  std::cout << "No se lanzó, call_once no lo intentará de nuevo\n";
}
 
void hacer_una_vez(bool lanzar)
{
  try {
    std::call_once(flag2, funcion_que_puede_lanzar, lanzar);
  }
  catch (...) {
  }
}
 
int main()
{
    std::thread st1(una_vez_sencillo);
    std::thread st2(una_vez_sencillo);
    std::thread st3(una_vez_sencillo);
    std::thread st4(una_vez_sencillo);
    st1.join();
    st2.join();
    st3.join();
    st4.join();
 
    std::thread t1(hacer_una_vez, true);
    std::thread t2(hacer_una_vez, true);
    std::thread t3(hacer_una_vez, false);
    std::thread t4(hacer_una_vez, true);
    t1.join();
    t2.join();
    t3.join();
    t4.join();
}

Posible salida:

Ejemplo sencillo: llamado una vez
lanzar: call_once lo intentará de nuevo
lanzar: call_once lo intentará de nuevo
No se lanzó, call_once no lo intentará de nuevo

[editar] Informes de defectos

Los siguientes informes de defectos de cambio de comportamiento se aplicaron de manera retroactiva a los estándares de C++ publicados anteriormente.

ID Aplicado a Comportamiento según lo publicado Comportamiento correcto
LWG 2442 C++11 Los argumentos se copian y/o mueven antes de la invocación. No se realiza copia/movimiento.

[editar] Véase también

(C++11)
Objeto auxiliar para asegurarse que call_once invoque la función una sola vez.
(clase) [editar]
Documentación de C para call_once