, которая синхронизирует доступ к моему инициализированному флагу и, следовательно, предотвращает параллельное выполнение другой инициализации. Я передаю в
call_once
два аргумента:
boost::call_once(Conn::init, Conn::initFlag_);
Первым аргументом является адрес функции, которая будет выполнять инициализацию. Второй аргумент — это флаг. В данном случае несколько потоков могут попытаться
выполнить инициализацию, но только первый в этом преуспеет.
12.5. Передача аргумента функции потока
Проблема
Требуется передать аргумент в вашу функцию потока, однако средствами библиотеки Boost Threads предусматривается передача только функторов без аргументов.
Решение
Создайте адаптер функтора, который принимает ваши параметры и возвращает функтор без параметров. Адаптер функтора можно использовать там, где должен был бы быть функтор потока. Пример 12.6 показывает, как это можно сделать.
Пример 12.6. Передача аргументов функции потока
#include <iostream>
#include <string>
#include <functional>
#include <boost/thread/thread.hpp>
// typedef используется для того, чтобы приводимые ниже объявления лучше
// читались
typedef void (*WorkerFunPtr)(const std::string&);
template<typename FunT, // Тип вызываемой функции
typename ParamT> // тип ее параметра
struct Adapter {
Adapter(FunT f, ParamT& p) : // Сконструировать данный адаптер и
f_(f), p_(&p) {} // установить члены на значение функции и ее
// аргумента
void operator { // Просто вызов функции с ее аргументом
f_(*p_);
}
private:
FunT f_;
ParamT* p_; // Использовать адрес параметра. чтобы избежать лишнего
приходится решать принципиальную проблему, причем характерную не только для потоков или проекта Boost, а общую проблему, возникающую при необходимости передачи функтора с одной сигнатурой туда, где требуется другая сигнатура. Решение состоит в создании адаптера.
Синтаксис может показаться немного путаным, но фактически в примере 12.6 создается временный функтор, который может вызываться конструктором потока как функция без аргументов (требуется именно такая функция). Но прежде всего используйте
typedef
, чтобы указатель функции лучше воспринимался в тексте.
typedef void (*WorkerFunPtr)(const std::string&);
Это создает тип
WorkerFunPtr
, который является указателем на функцию, принимающую по ссылке аргумент типа
string
и возвращающую тип
void
. После этого я создал шаблон класса
Adapter
. Он обеспечивает инстанцирование динамического функтора. Обратите внимание на конструктор:
template<Typename FunT,
typename ParamT>
struct Adapter {
Adapter(FunT f, ParamT& p) : f_(f), p_(&p) {}
// ...
Конструктор только инициализирует два члена, которые могут быть любого типа, но нам нужно, чтобы это был указатель на функцию и некоторый параметр
p
любого типа. Ради повышения эффективности я сохраняю адрес параметра, а не его значение.
Теперь рассмотрим следующую строку главного потока.
. Это именно те два типа, которые являются членами адаптера
f_
и
p_
. Наконец,
Adapter
перегружает
operator
, поэтому он может вызываться как функция. Его вызов означает просто выполнение следующей функции.
f_(*p_);
Применяя шаблон класса
Adapter
, можно передавать аргументы функциям потока, причем делается это за счет лишь небольшого усложнения синтаксиса. Если требуется передавать еще один аргумент, просто добавьте дополнительный тип и переменную-член в шаблон
Adapter
. Этот подход привлекателен тем, что позволяет создавать набор шаблонов классов обобщенного адаптера и использовать их в различных контекстах.