Библиотека pthread не поддерживает эту операцию напрямую.
В качестве обходного пути вы можете определить мьютекс для защиты блокировки:
- Чтобы получить блокировку, сначала получите мьютекс, затем блокировку (при необходимости читайте или пишите), затем отпустите мьютекс. (Никогда не приобретайте замок, не держа мьютекс.)
- Чтобы снять блокировку, просто отпустите ее (здесь не требуется мьютекс).
- Чтобы обновить блокировку, получите мьютекс, снимите блокировку чтения, получите блокировку записи, затем отпустите мьютекс.
- Чтобы понизить блокировку, получите мьютекс, снимите блокировку записи, установите блокировку чтения, затем отпустите мьютекс.
Таким образом, никакой другой поток не может захватить блокировку записи, пока вы пытаетесь обновить ее. Тем не менее, ваш поток будет блокироваться, если другие потоки удерживают блокировку чтения при попытке обновления.
Кроме того, как уже упоминалось выше, если два потока пытаются обновить одну и ту же блокировку одновременно, вы столкнетесь с тупиком:
- T1 и T2 оба удерживают блокировку чтения.
- T1 желает обновить, получает мьютекс, снимает блокировку чтения и пытается получить блокировку записи. Это заблокировано T2.
- T2 желает обновить, пытается получить мьютекс и заблокирован T1.
Вывод из моих лекций по CS: невозможно надежно избежать тупиков. Для каждой предложенной стратегии существует как минимум один вариант использования, в котором стратегия нецелесообразна. Единственное, что вы можете сделать, это обнаружить условия взаимоблокировки (то есть, если вызов не удался с EDEADLK
) и убедиться, что ваш код подготовлен для решения этой ситуации. (Способ восстановления сильно зависит от вашего кода.)
Понижение рейтинга таким способом не подвержено взаимоблокировкам although, хотя понижение и одновременное обновление может привести к взаимоблокировке. Если только один из ваших потоков обновляет блокировку таким образом (и другие потоки сразу же при необходимости блокируют запись), также нет риска тупика¹.
Как уже говорили другие, получение блокировки записи немедленно, когда вам может понадобиться, будет альтернативой, которая не склонна к взаимной блокировке¹, но может излишне помешать другим операциям чтения выполняться одновременно.
Вывод: зависит от вашего кода.
Если фаза только для чтения короткая (то есть достаточно короткая, чтобы вы могли позволить себе блокировать другие операции чтения в течение этого времени), то я бы выбрал метод произвольной блокировки записи.
Если фаза только для чтения может длиться долго, а блокирование других операций чтения в течение этого времени недопустимо, перейдите к обновлению блокировки с защитой мьютекса, но либо ограничьте его одним потоком на блокировку («только T1 может обновить блокировку L42, но не другие потоки ») или предоставляют способ обнаружения и восстановления после взаимоблокировок.
¹ Если в игру не вступят ресурсы, кроме этого замка и его мьютекса