Приложение В.Краткое сравнение библиотек для написания параллельных программ
Поддержка параллелизма и многопоточности в языках программирования и библиотеках не является чем-то новым, хотя включение ее в стандарт С++ — действительно новшество. Например, в Java многопоточность поддерживалась уже в самой первой версии; на платформах, согласованных со стандартом POSIX, имеется интерфейс из языка С к средствам многопоточности, предоставляемым операционной системой, а язык Erlang поддерживает параллелизм на основе передачи сообщений. Существуют даже библиотеки классов для С++, например Boost, которые обертывают программный интерфейс к средствам многопоточности, существующим на данной платформе (будь то интерфейс POSIX С или нечто иное) и тем самым предоставляют переносимый интерфейс для всех поддерживаемых платформ.
Для тех, кто уже имеет опыт написания многопоточных приложений и хотел бы воспользоваться им для разработки программ на С++ с применением новых возможностей, в этом приложении проводится сравнение средств, имеющихся в Java, POSIX С, С++ с библиотекой Boost Thread Library и С++11. Даются также перекрестные ссылки на соответствующие главы этой книги.
Средство Java Posix C Boost Threads C++11 Глава Запуск потоков Класс java.lang.thread
Тип pthread_t
и соответствующие функции API: pthread_create()
, pthread_detach()
, pthread_join()
Класс boost::thread
и его функции-члены Класс std::thread
и его функции-члены Глава 2 Взаимное исключение Блоки synchronized
Тип pthread_mutex_t
и соответствующие функции API: pthread_mutex_lock()
, pthread_mutex_unlock()
и другие Класс boost::mutex
и его функции-члены, шаблоны boost::lock_guard<>
и boost::unique_lock<>
Класс std::mutex
и его функции-члены, шаблоны std::lock_guard<>
и std::unique_lock<>
Глава 3 Ожидание предиката Методы wait()
и notify()
класса java.lang.Object
, используемые внутри блоков synchronized
Тип pthread_cond_t
и соответствующие функции API: pthread_cond_wait()
, pthread_cond_timed_wait()
и другие Классы boost::condition_variable
и boost::condition_variable_any
и их функции-члены Классы std::condition_variable
и std::condition_variable_any
и их функции-члены Глава 4 Атомарные операции и модель памяти с учетом параллелизма volatile
-переменные, типы в пакете java.util.concurrent.atomic
Отсутствует Отсутствует Типы std::atomic_xxx
, шаблон класса std::atomic<>
, функция std::atomic_thread_fence()
Глава 5 Потокобезопасные контейнеры Контейнеры в пакете java.util.concurrent
. Отсутствует Отсутствует Отсутствует Главы 6 и 7 Будущие результаты Интерфейс java.util.concurrent.future
и ассоциированные с ним классы Отсутствует Шаблонные классы boost::unique_future<>
и boost::shared_future<>
Шаблонные классы std::future<>
, std::shared_future<>
, и std::atomic_future<>
Глава 9 Пулы потоков Класс java.util.concurrent.ThreadPoolExecutor
Отсутствует Отсутствует Отсутствует Глава 9 Прерывание потока Метод interrupt()
класса java.lang.Thread
pthread_cancel()
Функция-член interrupt()
класса boost::thread
Отсутствует
Приложение С.Каркас передачи сообщений и полный пример программы банкомата
В разделе 4.1 мы познакомились с каркасом передачи сообщений между потоками, продемонстрировав его на примере программы банкомата. В этом приложении приводится полный код примера, включая и код каркаса передачи сообщений.
В листинге С.1 показан код очереди сообщений. Сообщения хранятся в списке и представлены указателями на базовый класс. Сообщения конкретного типа обрабатываются шаблонным классом, производным от этого базового класса. В момент помещения сообщения в очередь конструируется подходящий экземпляр обертывающего класса и сохраняется указатель на него; операция извлечения возвращает именно этот указатель. Поскольку в классе
message_base
нет функций-членов, извлекающий поток должен привести указатель к нужному типу wrapped_message
, прежде чем сможет получить хранящееся сообщение.
Листинг С.1. Простая очередь сообщений
#include
#include
#include
#include
namespace messaging
{ │
Базовый классstruct message_base {←┘
элементов очереди virtual ~message_base() {}
};
template │
Для каждого типа сообщенийstruct wrapped_message:←┘
имеется специализацияmessage_base {
Msg contents;
explicit wrapped_message(Msg const& contents_):
contents(contents_) {}
};
│
Наша очередьclass queue←┘
сообщений{ │
В настоящей std::mutex m; │
очереди хранят- std::condition_variable с; │
ся указатели на std::queue> q;←┘
message_base
public:
template│
Обернуть добав- void push(T const& msg) │
ленное сообще- { │
ние и сохранить std::lock_guard lk(m);│
указатель q.push( ←┘
std::make_shared>(msg));
с.notify_all();
}
std::shared_ptr wait_and_pop()│
Блокирует до { │
появления в std::unique_lock lk(m); │
очереди хотя бы c.wait(lk, [&]{ return !q.empty(); }); ←┘
одного элемента auto res = q.front();
q.pop();
return res;
}
};
}
Отправкой сообщений занимается объект класса
sender
, показанного в листинге С.2. Это не более чем тонкая обертка вокруг очереди сообщений, которая позволяет только добавлять сообщения. При копировании экземпляров sender
копируется только указатель на очередь, а не сама очередь.
Листинг С.2. Класс
sender
namespace messaging {
class sender {│
sender обертывает указатель queue* q; ←┘
на очередь
public: │
У сконструированного по умолчанию sender() :←┘
sender'a нет очереди q(nullptr) {}
│
Разрешаем конструирование explicit sender(queue* q_):←┘
из указателя на очередь q(q_) {}
template
void send(Message const