pthreads: блокировки чтения / записи, обновление блокировки чтения до блокировки записи - PullRequest
14 голосов
/ 09 марта 2010

Я использую блокировки чтения / записи в Linux и обнаружил, что пытаюсь обновить заблокированный объект чтения до взаимоблокировок блокировки записи.

т.е.

// acquire the read lock in thread 1.
pthread_rwlock_rdlock( &lock );

// make a decision to upgrade the lock in threads 1.
pthread_rwlock_wrlock( &lock ); // this deadlocks as already hold read lock.

Я прочитал справочную страницу, и она довольно специфическая.

Вызывающий поток может зайти в тупик, если в время звонка удерживает блокировка чтения-записи (чтение или блокировка записи).

Каков наилучший способ обновить блокировку чтения до блокировки записи в этих обстоятельствах ... Я не хочу приводить гонку к защищаемой переменной.

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

Thx

Ответы [ 4 ]

16 голосов
/ 09 марта 2010

Что еще, кроме мертвой блокировки, вы хотите в следующем сценарии?

  • поток 1 получает блокировку чтения
  • поток 2 получает блокировку чтения
  • поток 1 попросить обновить блокировку для записи
  • поток 2 попросить обновить блокировку для записи

Так что я бы просто снял блокировку чтения, установил блокировку записи и снова проверил, нужно ли мне обновить или нет.

1 голос
/ 09 марта 2010

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

Я был немного удивлен, читая этот вопрос, потому что я даже не думал, что сначала взяли блокировку чтения, а затем обновили до блокировки записи. Ну, для другой ситуации могут потребоваться разные подходы.

0 голосов
/ 01 апреля 2019

Библиотека pthread не поддерживает эту операцию напрямую.

В качестве обходного пути вы можете определить мьютекс для защиты блокировки:

  • Чтобы получить блокировку, сначала получите мьютекс, затем блокировку (при необходимости читайте или пишите), затем отпустите мьютекс. (Никогда не приобретайте замок, не держа мьютекс.)
  • Чтобы снять блокировку, просто отпустите ее (здесь не требуется мьютекс).
  • Чтобы обновить блокировку, получите мьютекс, снимите блокировку чтения, получите блокировку записи, затем отпустите мьютекс.
  • Чтобы понизить блокировку, получите мьютекс, снимите блокировку записи, установите блокировку чтения, затем отпустите мьютекс.

Таким образом, никакой другой поток не может захватить блокировку записи, пока вы пытаетесь обновить ее. Тем не менее, ваш поток будет блокироваться, если другие потоки удерживают блокировку чтения при попытке обновления.

Кроме того, как уже упоминалось выше, если два потока пытаются обновить одну и ту же блокировку одновременно, вы столкнетесь с тупиком:

  • T1 и T2 оба удерживают блокировку чтения.
  • T1 желает обновить, получает мьютекс, снимает блокировку чтения и пытается получить блокировку записи. Это заблокировано T2.
  • T2 желает обновить, пытается получить мьютекс и заблокирован T1.

Вывод из моих лекций по CS: невозможно надежно избежать тупиков. Для каждой предложенной стратегии существует как минимум один вариант использования, в котором стратегия нецелесообразна. Единственное, что вы можете сделать, это обнаружить условия взаимоблокировки (то есть, если вызов не удался с EDEADLK) и убедиться, что ваш код подготовлен для решения этой ситуации. (Способ восстановления сильно зависит от вашего кода.)

Понижение рейтинга таким способом не подвержено взаимоблокировкам although, хотя понижение и одновременное обновление может привести к взаимоблокировке. Если только один из ваших потоков обновляет блокировку таким образом (и другие потоки сразу же при необходимости блокируют запись), также нет риска тупика¹.

Как уже говорили другие, получение блокировки записи немедленно, когда вам может понадобиться, будет альтернативой, которая не склонна к взаимной блокировке¹, но может излишне помешать другим операциям чтения выполняться одновременно.

Вывод: зависит от вашего кода.

Если фаза только для чтения короткая (то есть достаточно короткая, чтобы вы могли позволить себе блокировать другие операции чтения в течение этого времени), то я бы выбрал метод произвольной блокировки записи.

Если фаза только для чтения может длиться долго, а блокирование других операций чтения в течение этого времени недопустимо, перейдите к обновлению блокировки с защитой мьютекса, но либо ограничьте его одним потоком на блокировку («только T1 может обновить блокировку L42, но не другие потоки ») или предоставляют способ обнаружения и восстановления после взаимоблокировок.


¹ Если в игру не вступят ресурсы, кроме этого замка и его мьютекса

0 голосов
/ 01 декабря 2016

Я думаю, что вместо использования блокировки чтения / записи pthread, вы можете использовать Posix fcntl (). Здесь вы можете перейти от чтения к записи без каких-либо хлопот. Мы используем его для вставки B-дерева. Как только мы узнаем узел, где происходит вставка, мы обновим его до блокировки записи. Также, когда нам нужно разделить узел, мы обновим блокировку узла, его родительского узла и дочерних элементов с чтения на запись. Поскольку B-дерево - это структура данных, основанная на файлах, она помогает захватить область файла.

...