C++. Сборник рецептов
Шрифт:
Здесь необходимо отметить несколько моментов. Обратите внимание,
При использовании мьютексов чтения/записи блокировки тоже выполняются иначе. В примере 12.3, когда мне нужно заблокировать
А когда мне просто требуется прочитать
Блокировки чтения/записи удобны, но они не предохраняют вас от серьезных ошибок. На этапе компиляции не делается проверка ресурса, представленного мьютексом
Точная последовательность выполнения блокировок определяется политикой их планирования; эту политику вы задаете при конструировании объекта mutex. В библиотеке Boost Threads предусматривается четыре политики.
Потоки, ожидающие выполнения блокировки для чтения, ее получат раньше потоков, ожидающих выполнения блокировки для записи.
Потоки, ожидающие выполнения блокировки для записи, ее получат раньше потоков, ожидающих выполнения блокировки для чтения.
Чередуются блокировки для чтения и для записи. Один читающий поток получает возможность блокировки для чтения, когда подходит «очередь» читающих потоков. Эта политика в целом отдает приоритет записывающим потокам. Например, если мьютекс заблокирован для записи и имеется несколько потоков, ожидающих блокировки для чтения, а также один поток, ожидающий блокировки для записи, сначала будет выполнена одна блокировка для чтения, затем блокировка для записи и после нее — все остальные блокировки для чтения. Подразумевается, что за это время не будет новых запросов на блокировку.
Чередуются блокировки для чтения и для записи. Выполняются все блокировки для чтения, когда подходит «очередь» читающих потоков. Другими словами, эта политика приводит к опустошению очереди всех потоков, ожидающих блокировки для чтения, в промежутке между блокировками для записи.
Каждая из этих политик имеет свои достоинства и недостатки, и их влияние будет сказываться по-разному в различных приложениях. Необходимо тщательно подойти к выбору политики, потому что если просто обеспечить приоритет для чтения или записи, это приведет к зависанию, которое я более подробно описываю ниже.
При программировании многопоточной обработки возникает три основные проблемы: взаимная блокировка (deadlock), зависание (starvation) и состояния состязания (race conditions — условия гонок). Существуют различные по сложности методы устранения этих проблем, но их рассмотрение выходит за рамки данного рецепта. Я дам описание каждой их этих проблем, чтобы вы знали, чего следует остерегаться, но если вы планируете разработку многопоточного приложения, вам необходимо сначала выполнить некоторое предварительную работу по шаблонам многопоточной обработки.
Взаимная блокировка связана с наличием, по крайней мере, двух потоков и двух ресурсов. Пусть имеется два потока, А и В, и два ресурса, X и Y, причем поток А блокирует ресурс X, а В блокирует Y. Взаимная блокировка возникает в том случае, когда А пытается заблокировать Y, а В пытается
заблокировать X. Если при работе потоков не предусмотреть какой-либо способ устранения взаимных блокировок, они будут ждать бесконечно.Библиотека Boost Threads позволяет избегать взаимных блокировок благодаря уточнению концепций мьютекса и блокировки. Пробный мьютекс (try mutex) — это мьютекс, который используется для определения возможности блокировки путем выполнения пробной блокировки (try lock); она может быть успешной или нет, но не блокирует ресурс, а ждет момента, когда блокировка станет возможной. Применяя модели этих концепций в форме классов
Например, в классе
Используемые здесь мьютекс и блокировка отличаются от тех, которые применялись в примере 12.2. Убедитесь, что используемые вами имена классов мьютекса и блокировки правильно квалифицированы, в противном случае вы получите не то, на что рассчитываете.
При сериализации доступа к чему-либо вы заставляете пользователей этого ресурса выстраиваться друг за другом и дожидаться свой очереди. Если положение пользователей ресурса в очереди остается неизменным, каждый из них имеет шанс получения доступа к ресурсу. Однако если некоторым пользователям разрешается сокращать свою очередь, то до находящихся в конце очередь может никогда не дойти. Возникает зависание.
При использовании мьютекса mutex пользователи ресурса, которые находятся в состоянии ожидания, образуют группу, а не последовательность. Нельзя сказать, что существует определенный порядок между потоками, ожидающими возможности выполнения блокировки. Для мьютексов чтения/записи в библиотеке Boost Threads используется четыре политики планирования блокировок, которые были описаны ранее. Поэтому при использовании мьютексов чтения/записи необходимо понимать смысл различных политик планирования и действий ваших потоков. Если вы используете политику