Параллельное программирование на С++ в действии. Практика разработки многопоточных программ | страница 67



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

>std::shared_ptr resource_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()