Использование мьютекса позволяет сделать всю работу, однако хочется немного большего. При таком подходе нет различия между чтением и записью, что существенно, так как неэффективно заставлять потоки ждать в очереди доступа к ресурсу, когда многие из них выполняют только операции чтения, для которых не требуется монопольный доступ. Для этого в библиотеке Boost Threads предусмотрен класс read_write_mutex. Пример 12.3 показывает, как можно реализовать пример 12.2, используя read_write_mutex с функцией-членом front, которая позволяет вызывающей программе получить копию первого элемента очереди без его выталкивания.

Пример 12.3. Использование мьютекса чтения/записи

#include <iostream>

#include <boost/thread/thread.hpp>

#include <boost/thread/read_write_mutex.hpp>

#include <string>

template<typename T>

class Queue {

 public:

 Queue() : // Использовать мьютекс чтения/записи и придать ему приоритет

           // записи

 rwMutex_(boost::read_write_scheduling_policy::writer_priority) {}

 ~Queue() {}

 void enqueue(const T& x) {

  // Использовать блокировку чтения/записи, поскольку enqueue

  // обновляет состояние

  boost::read_write_mutex::scoped_write_lock writeLock (rwMutex_);

  list_.push_back(x);

 }

 T dequeue() {

  // Снова использовать блокировку для записи

  boost::read_write_mutex::scoped_write_lock writeLock (rwMutex_);

  if (list_.empty())

   throw 'empty!';

  T tmp = list_.front();

  list_.pop_front();

  return(tmp);

 }

 T getFront() {

  // Это операция чтения, поэтому требуется блокировка только для чтения

  boost::read_write_mutex::scoped_read_lock.readLock (rwMutex_);

  if (list_.empty())

   throw 'empty!';

  return(list_.front());

 }

private:

 std::list<T> list_;

 boost::read_write_mutex rwMutex_;

};

Queue<std::string> queueOfStrings;

void sendSomething() {

 std::string s;

 for (int i = 0, i < 10; ++i) {

  queueOfStrings.enqueue('Cyrus');

 }

}

void checkTheFront() {

 std::string s;

 for (int i=0; i < 10; ++i) {

  try {

   s = queueOfStrings.getFront();

  } catch(...) {}

 }

}

int main() {

 boost::thread thr1(sendSomething);

 boost::thread_group grp;

 grp.сreate_thread(checkTheFront);

 grp.create_thread(checkTheFront);

 grp.сreate_thread(checkTheFront);

 grp_create_thread(checkTheFront);

 thr1.join();

 grp.join_all();

}

Здесь необходимо отметить несколько моментов. Обратите внимание, что теперь я использую read_write_mutex.

boost::read_write_mutex rwMutex_;

При использовании мьютексов чтения/записи блокировки тоже выполняются иначе. В примере 12.3, когда мне нужно заблокировать Queue для записи, я создаю объект класса scoped_write_lock.

boost::read_write_mutex::scoped_write_lock writeLock(rwMutex_);

А когда мне просто требуется прочитать Queue, я использую scoped_read_lock.

boost::read_write_mutex::scoped_read_lock readLock(rwMutex_);

Блокировки чтения/записи удобны, но они не предохраняют вас от серьезных ошибок. На этапе компиляции не делается проверка ресурса, представленного мьютексом rwMutex_, гарантирующая отсутствие изменения ресурса при блокировке только для чтения. Вы сами должны позаботиться о том, чтобы поток мог модифицировать состояние объекта только при блокировке для записи, поскольку компилятор это не будет делать.

Точная последовательность выполнения блокировок определяется политикой их планирования; эту политику вы задаете при конструировании объекта mutex. В библиотеке Boost Threads предусматривается четыре политики.

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ОБРАНЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату