Функция call_once
Функция call_once гарантирует, что переданная ей функция будет вызвана ровно один раз,
даже если несколько потоков попытаются вызвать её одновременно. Это особенно полезно
для ленивой инициализации объектов или выполнения однократных настроек в многопоточной среде.
В первый параметр мы передаем объект типа once_flag, который хранит состояние вызова,
а во второй - вызываемую функцию (или лямбду, или функтор). Дополнительные аргументы
можно передать после второго параметра - они будут переданы в вызываемую функцию.
Потоки, которые вызывают call_once с одним и тем же объектом once_flag,
будут заблокированы до тех пор, пока первый поток не завершит выполнение переданной функции.
Если функция завершится с исключением, то блокировка снимается, и следующий поток
попытается выполнить функцию снова.
Синтаксис
#include <mutex>
void call_once(once_flag& flag, Callable&& func, Args&&... args);
Пример
Давайте рассмотрим простой пример, где несколько потоков пытаются инициализировать общий ресурс только один раз:
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
once_flag flag;
int shared_value = 0;
void init()
{
shared_value = 42;
cout << "Initialized: " << shared_value << endl;
}
void worker()
{
call_once(flag, init);
cout << "Worker sees: " << shared_value << endl;
}
int main()
{
thread t1(worker);
thread t2(worker);
thread t3(worker);
t1.join();
t2.join();
t3.join();
return 0;
}
Результат выполнения кода:
"Initialized: 42"
"Worker sees: 42"
"Worker sees: 42"
"Worker sees: 42"
Пример
Давайте передадим дополнительные аргументы в функцию через call_once:
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
once_flag flag;
void setup(int a, int b)
{
cout << "Setup: " << a + b << endl;
}
void worker()
{
call_once(flag, setup, 10, 20);
}
int main()
{
thread t1(worker);
thread t2(worker);
t1.join();
t2.join();
return 0;
}
Результат выполнения кода:
"Setup: 30"
Смотрите также
-
класс
once_flag,
который хранит состояние однократного вызова -
класс
mutex,
который обеспечивает базовую блокировку -
класс
lock_guard,
который автоматически управляет блокировкой -
класс
unique_lock,
который предоставляет гибкое управление блокировкой