Параллельное программирование на С++ в действии. Практика разработки многопоточных программ | страница 67
>std::call_once
. В данном случае инициализация производится путем вызова функции, но ничто не мешает завести для той же цели класс, в котором определен оператор вызова. Как и большинство функций в стандартной библиотеке, принимающих в качестве аргументов функции или предикаты, >std::call_once
работает как с функциями, так и с объектами, допускающими вызов.>std::shared_ptr
>std::once_flag resource_flag;←
(1)
>void init_resource() {
> resource_ptr.reset(new some_resource);
>}
> │
Инициализация производится
>void foo() { ←┘
ровно один раз
> std::call_once(resource_flag, init_resource);
> resource_ptr->do_something();
>}
Здесь переменная типа >std::once_flag
(1) и инициализируемый объект определены в области видимости пространства имен, но >std::call_once()
вполне можно использовать и для отложенной инициализации членов класса, как показано в следующем листинге.
Листинг 3.12. Потокобезопасная отложенная инициализация члена класса с помощью функции >std::call_once()
>class X {
>private:
> connection_infо connection_details;
> connection_handle connection;
> std::once_flag connection_init_flag;
> void open_connection() {
> connection = connection_manager.open(connection_details);
> }
>public:
> X(connection_info const& connection_details_):
> connection_details(connection_details_) {}
> void send_data(data_packet const& data)←
(1)
> {
> std::call_once(
> connection_init_flag, &X::open_connection, this);←┐
> connection.send_data(data); │
> } │
> data_packet receive_data() { ←
(3)
> std::call_once( │
> connection_init_flag, &X::open_connection, 2)
(2)
> this); ←┘
> return connection.receive_data();
> }
>};
В этом примере инициализация производится либо при первом обращении к >send_data()
(1), либо при первом обращении к >receive_data()
(3). Поскольку данные инициализируются функцией-членом >open_connection()
, то требуется передавать также указатель >this
. Как и во всех функциях из стандартной библиотеки, которые принимают объекты, допускающие вызов, (например, конструктор >std::thread
и функция >std::bind()
), это делается путем передачи >std::call_once()