} // разблокировано!

Когда lock уничтожается, mutex_ разблокируется. Если lock конструируется для объекта mutex, который уже заблокирован другим потоком, текущий поток переходит в состояние ожидания до тех пор, пока lock не окажется доступен.

Такой подход поначалу может показаться немного странным: а почему бы мьютексу mutex не иметь методы lock и unlock? Применение класса scoped_lock, который обеспечивает блокировку при конструировании и разблокировку при уничтожении, на самом деле более удобно и менее подвержено ошибкам. Когда вы создаете блокировку, используя scoped_lock, мьютекс блокируется на весь период существования объекта scoped_lock, т.е. вам не надо ничего разблокировать в явной форме на каждой ветви вычислений. С другой стороны, если вам приходится явно разблокировать захваченный мьютекс, необходимо гарантировать перехват любых исключений, которые могут быть выброшены в вашей функции (или где-нибудь выше ее в стеке вызовов), и гарантировать разблокировку mutex. При использовании scoped_lock, если выбрасывается исключение или функция возвращает управление, объект scoped_lock автоматически уничтожается и mutex разблокируется.

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

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

#include

#include

#include

#include

template

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 list_;

 boost::read_write_mutex rwMutex_;

};

Queue 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_;

Перейти на страницу:

Похожие книги