Класс once_flag
Класс once_flag используется в паре с функцией
call_once и позволяет гарантировать,
что определенный участок кода будет выполнен
ровно один раз, независимо от количества потоков,
которые пытаются его выполнить. Объект
once_flag не хранит никаких пользовательских
данных - он лишь служит флагом состояния,
который внутренне синхронизируется.
Класс не поддерживает копирование и перемещение,
поэтому его следует передавать по ссылке.
Основное применение - однократная инициализация общих ресурсов (например, подключение к базе данных, загрузка конфигурации, создание синглтона) в многопоточной среде.
Синтаксис
#include <mutex>
std::once_flag flag;
std::call_once(flag, func, args...);
Пример
Давайте рассмотрим простой пример однократной инициализации общего ресурса из двух потоков:
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
once_flag flag;
void init()
{
cout << "init once" << endl;
}
int main()
{
thread t1([]{ call_once(flag, init); });
thread t2([]{ call_once(flag, init); });
t1.join();
t2.join();
return 0;
}
Результат выполнения кода:
"init once"
Пример
Давайте используем once_flag вместе
с лямбда-выражением для инициализации
глобальной переменной:
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
once_flag flag;
int shared_value = 0;
int main()
{
thread t1([]
{
call_once(flag, []
{
shared_value = 42;
cout << "initialized" << endl;
});
cout << shared_value << endl;
});
thread t2([]
{
call_once(flag, []
{
shared_value = 42;
cout << "initialized" << endl;
});
cout << shared_value << endl;
});
t1.join();
t2.join();
return 0;
}
Результат выполнения кода:
"initialized"
42
42
Пример
Давайте проверим, что при вызове из нескольких потоков функция выполнится ровно один раз, используя счетчик:
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
using namespace std;
once_flag flag;
int counter = 0;
void increment()
{
++counter;
}
int main()
{
vector<thread> threads;
for (int i = 0; i < 5; ++i)
{
threads.emplace_back([]
{
call_once(flag, increment);
});
}
for (auto& t : threads)
{
t.join();
}
cout << counter << endl;
return 0;
}
Результат выполнения кода:
1
Смотрите также
-
функция
call_once,
которая выполняет код ровно один раз -
класс
mutex,
который обеспечивает базовую синхронизацию -
класс
lock_guard,
который автоматически блокирует и разблокирует мьютекс -
класс
unique_lock,
который предоставляет гибкое управление блокировкой