Чтение онлайн

на главную

Жанры

C++. Сборник рецептов

Когсуэлл Джефф

Шрифт:

Этот фрагмент программного кода хорошо работает в однопоточной среде, потому что

q
не может быть модифицирован в промежутке между первой и второй строкой. Однако в условиях многопоточной обработки, когда практически в любой момент другой поток может модифицировать
q
, следует исходить из предположения, что совместно используемые объекты модифицируются, когда поток не блокирует доступ к ним. После строки 1 другой поток, работая параллельно, может извлечь следующий элемент из
q
при помощи функции
dequeue
, что означает получение в строке 2 чего-то неожиданного или совсем ничего. Как функция
getFront
,
так и функция
dequeue
блокирует один объект
mutex
, используемый для модификации
q
, но между их вызовами мьютекс разблокирован, и, если другой поток находится в ожидании выполнения блокировки, он может это сделать до того, как получит свой шанс строка 2.

Проблема состояния состязания в этом конкретном случае решается путем гарантирования сохранения блокировки на весь период выполнения операции. Создайте функцию-член

dequeueIfEquals
, которая извлекает следующий объект из очереди, если он равен аргументу. Функция
dequeueIfEquals
может использовать блокировку, как и всякая другая функция.

T dequeueIfEquals(const T& t) {

 boost::mutex::scoped_lock lock(mutex_);

 if (list_.front == t)

 // ...

Существуют состояния состязания другого типа, но этот пример должен дать общее представление о том, чего следует остерегаться. По мере увеличения количества потоков и совместно используемых ресурсов состояния состязания оказываются более изощренными и обнаруживать их сложнее. Поэтому следует быть особенно осторожным на этапе проектирования, чтобы не допускать их.

В многопоточной обработке самое сложное — гарантировать сериализованный доступ к ресурсам, потому что если это сделано неправильно, отладка становится кошмаром. Поскольку многопоточная программа по своей сути недетерминирована (так как потоки могут выполняться в различной очередности и с различными квантами времени при каждом новом выполнении программы), очень трудно точно обнаружить место и способ ошибочной модификации чего-либо. Здесь еще в большей степени, чем в однопоточном программировании, надежный проект позволяет минимизировать затраты на отладку и переработку.

12.3. Уведомление одного потока другим

Проблема

Используется шаблон, в котором один поток (или группа потоков) выполняет какие-то действия, и требуется сделать так, чтобы об этом узнал другой поток (или группа потоков). Может использоваться главный поток, который передает работу подчиненным потокам, или может использоваться одна группа потоков для пополнения очереди и другая для удаления данных из очереди и выполнения чего-либо полезного.

Решение

Используйте объекты

mutex
и
condition
, которые объявлены в boost/thread/mutex.hpp и boost/thread/condition.hpp. Можно создать условие (
condition
) для каждой ожидаемой потоками ситуации и при возникновении такой ситуации уведомлять все ее ожидающие потоки. Пример 12.4 показывает, как можно обеспечить передачу уведомлений в модели потоков «главный/подчиненные».

Пример 12.4. Передача уведомлений между потоками

#include <iostream>

#include <boost/thread/thread.hpp>

#include <boost/thread/condition.hpp>

#include <boost/thread/mutex.hpp>

#include <list>

#include <string>

class Request { /*...*/ };

//
Простой класс очереди заданий; в реальной программе вместо этого класса

// используйте std::queue

template<typename T>

class JobQueue {

public:

 JobQueue {}

 ~JobQueue {}

 void submitJob(const T& x) {

boost::mutex::scoped_lock lock(mutex_);

list_.push_back(x);

workToBeDone_.notify_one;

 }

 T getJob {

boost::mutex::scoped_lock lock(mutex_);

workToBeDone_.wait(lock); // Ждать удовлетворения этого условия, затем

// блокировать мьютекс

T tmp = list_.front;

list_.pop_front;

return(tmp);

 }

private:

 std::list<T> list_;

 boost::mutex mutex_;

 boost::condition workToBeDone_;

};

JobQueue<Request> myJobQueue;

void boss {

 for (;;) {

// Получить откуда-то запрос

Request req;

myJobQueue.submitJob(req);

 }

}

void worker {

 for (;;) {

Request r(myJobQueue.getJob);

// Выполнить какие-то действия с заданием...

 }

}

int main {

 boost::thread thr1(boss);

 boost::thread thr2(worker);

 boost::thread thr3(worker);

 thr1.join;

 thr2.join;

 thr3.join;

}

Обсуждение

Объект условия использует мьютекс

mutex
и позволяет дождаться ситуации, когда он становится заблокированным. Рассмотрим пример 12.4, в котором представлена модифицированная версии класса
Queue
из примера 12.2. Я модифицировал очередь
Queue
, получая более специализированную очередь, а именно
JobQueue
, объекты которой являются заданиями, поступающими в очередь со стороны главного потока и обрабатываемыми подчиненными потоками.

Поделиться:
Популярные книги

Жандарм 4

Семин Никита
4. Жандарм
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Жандарм 4

Прорвемся, опера! Книга 2

Киров Никита
2. Опер
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Прорвемся, опера! Книга 2

Шайтан Иван 2

Тен Эдуард
2. Шайтан Иван
Фантастика:
боевая фантастика
попаданцы
альтернативная история
5.00
рейтинг книги
Шайтан Иван 2

Архил...? 4

Кожевников Павел
4. Архил...?
Фантастика:
фэнтези
попаданцы
альтернативная история
5.50
рейтинг книги
Архил...? 4

Идеальный мир для Лекаря 6

Сапфир Олег
6. Лекарь
Фантастика:
фэнтези
юмористическая фантастика
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря 6

Вперед в прошлое!

Ратманов Денис
1. Вперед в прошлое
Фантастика:
попаданцы
5.00
рейтинг книги
Вперед в прошлое!

Кротовский, не начинайте

Парсиев Дмитрий
2. РОС: Изнанка Империи
Фантастика:
городское фэнтези
попаданцы
альтернативная история
5.00
рейтинг книги
Кротовский, не начинайте

Крестоносец

Ланцов Михаил Алексеевич
7. Помещик
Фантастика:
героическая фантастика
попаданцы
альтернативная история
5.00
рейтинг книги
Крестоносец

Фиктивный брак

Завгородняя Анна Александровна
Фантастика:
фэнтези
6.71
рейтинг книги
Фиктивный брак

Николай I Освободитель. Книга 2

Савинков Андрей Николаевич
2. Николай I
Фантастика:
героическая фантастика
альтернативная история
5.00
рейтинг книги
Николай I Освободитель. Книга 2

Убивать чтобы жить 2

Бор Жорж
2. УЧЖ
Фантастика:
героическая фантастика
боевая фантастика
рпг
5.00
рейтинг книги
Убивать чтобы жить 2

Корпулентные достоинства, или Знатный переполох. Дилогия

Цвик Катерина Александровна
Фантастика:
юмористическая фантастика
7.53
рейтинг книги
Корпулентные достоинства, или Знатный переполох. Дилогия

Барон устанавливает правила

Ренгач Евгений
6. Закон сильного
Старинная литература:
прочая старинная литература
5.00
рейтинг книги
Барон устанавливает правила

Отдельный танковый

Берг Александр Анатольевич
1. Антиблицкриг
Фантастика:
боевая фантастика
альтернативная история
5.00
рейтинг книги
Отдельный танковый