Параллельное программирование на С++ в действии — страница 40 из 53

Приложение В.Краткое сравнение библиотек для написания параллельных программ

Поддержка параллелизма и многопоточности в языках программирования и библиотеках не является чем-то новым, хотя включение ее в стандарт С++ — действительно новшество. Например, в Java многопоточность поддерживалась уже в самой первой версии; на платформах, согласованных со стандартом POSIX, имеется интерфейс из языка С к средствам многопоточности, предоставляемым операционной системой, а язык Erlang поддерживает параллелизм на основе передачи сообщений. Существуют даже библиотеки классов для С++, например Boost, которые обертывают программный интерфейс к средствам многопоточности, существующим на данной платформе (будь то интерфейс POSIX С или нечто иное) и тем самым предоставляют переносимый интерфейс для всех поддерживаемых платформ.

Для тех, кто уже имеет опыт написания многопоточных приложений и хотел бы воспользоваться им для разработки программ на С++ с применением новых возможностей, в этом приложении проводится сравнение средств, имеющихся в Java, POSIX С, С++ с библиотекой Boost Thread Library и С++11. Даются также перекрестные ссылки на соответствующие главы этой книги.

СредствоJavaPosix CBoost ThreadsC++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