Проблема читателей – писателей - Readers–writers problem

В Информатика, то читатели – писатели проблемы являются примерами общей вычислительной проблемы в параллелизм. Существует по крайней мере три варианта проблем, которые связаны с ситуациями, в которых множество одновременных потоки выполнения пытаются получить доступ к одному и тому же общему ресурсу одновременно.

Некоторые потоки могут читать, а некоторые писать, с ограничением, что ни один поток не может обращаться к общему ресурсу для чтения или записи, пока другой поток выполняет запись в него. (В частности, мы хотим предотвратить одновременное изменение общего ресурса более чем одним потоком и позволить двум или более читателям получать доступ к общему ресурсу одновременно). А читатель-писатель замок это структура данных который решает одну или несколько проблем читателей и писателей.

Основная проблема читателя и писателя была впервые сформулирована и решена Куртуа. и другие.[1][2]

Проблема первых читателей – писателей

Предположим, у нас есть общая область памяти (критическая секция) с основными ограничениями, описанными выше. Можно защитить общие данные за счет взаимного исключения мьютекс, и в этом случае никакие два потока не могут получить доступ к данным одновременно. Однако это решение неоптимально, потому что читатель может р1 может быть замок, а затем другой читатель р2 запрашивает доступ. Было бы глупо для р2 ждать пока р1 было выполнено перед запуском собственной операции чтения; вместо, р2 должно быть разрешено читать ресурс вместе с р1 поскольку чтение не изменяет данные, поэтому одновременное чтение безопасно. Это мотивация для проблема первых читателей – писателей, в котором добавлено ограничение, что ни один читатель не должен ждать, если в данный момент акция открыта для чтения. Это также называется читательские предпочтения, с его решением:

 1 семафор ресурс=1; 2 семафор rmutex=1; 3 счетчик=0; 4  5 /* 6    resource.P () эквивалентен wait (resource) 7    resource.V () эквивалентен signal (resource) 8    rmutex.P () эквивалентно wait (rmutex) 9    rmutex.V () эквивалентен signal (rmutex)10 */11 12 писатель() {13     ресурс.п();          // Заблокировать общий файл для писателя14 15     <КРИТИЧЕСКИЙ Раздел>16     // Запись завершена17 18     <ВЫХОД Раздел>19     ресурс.V();          // Освободить общий файл для использования другими читателями. Писатели допускаются, если нет читателей, запрашивающих это.20 }21 22 читатель() {23     rmutex.п();           // Убедитесь, что ни один другой читатель не может выполнить раздел , пока вы в нем24     <КРИТИЧЕСКИЙ Раздел>25     счетчик++;          // Указываем, что вы читатель, пытающийся войти в Критический раздел26     если (счетчик == 1)   // Проверяет, являетесь ли вы первым читателем, пытающимся войти в CS27         ресурс.п();     // Если вы первый читатель, заблокируйте ресурс от писателей. Ресурс остается зарезервированным для последующих читателей28     <ВЫХОД КРИТИЧЕСКИЙ Раздел>29     rmutex.V();           //Релиз30 31     // Читаем32 33     rmutex.п();           // Убедитесь, что ни один другой читатель не может выполнить раздел , пока вы в нем34     <КРИТИЧЕСКИЙ Раздел>35     счетчик--;          // Указываем, что вам больше не нужен общий ресурс. На одного читателя меньше36     если (счетчик == 0)   // Проверяет, являетесь ли вы последним (единственным) читателем, который читает общий файл37         ресурс.V();     // Если вы последний читатель, то можете разблокировать ресурс. Это делает его доступным для писателей.38     <ВЫХОД КРИТИЧЕСКИЙ Раздел>39     rmutex.V();           //Релиз40 }

В этом решении проблемы читателей / писателей первый читатель должен заблокировать ресурс (общий файл), если он доступен. После того, как файл заблокирован от авторов, он может быть использован многими последующими читателями без необходимости повторной блокировки его.

Перед входом в критическая секция, каждый новый читатель должен пройти через раздел входа. Однако в разделе ввода одновременно может быть только один читатель. Это сделано, чтобы избежать условия гонки на считывателях (в этом контексте состояние гонки - это состояние, при котором два или более потока одновременно просыпаются и пытаются войти в критическую секцию; без дополнительных ограничений поведение недетерминировано. Например, два считывателя увеличивают счетчик чтения одновременно время, и оба пытаются заблокировать ресурс, в результате чего блокируется один читатель). Для этого каждый читатель, который входит в , блокирует для себя, пока не закончит с ним. На этом этапе читатели не блокируют ресурс. Они только блокируют раздел ввода, поэтому другие читатели не могут войти в него, пока они в нем. Как только читатель закончит выполнение раздела ввода, он разблокирует его, сигнализируя мьютекс. Сигнализация эквивалентна: mutex.V () в приведенном выше коде. То же самое действительно для <Раздел ВЫХОДА>. В секции выхода одновременно может быть не более одного читателя, поэтому каждый читатель должен потребовать и заблокировать для себя секцию выхода перед ее использованием.

Как только первый читатель окажется в разделе ввода, он заблокирует ресурс. Это предотвратит доступ к нему писателей. Последующие читатели могут просто использовать заблокированный (от авторов) ресурс. Читатель, закончивший последним (обозначенный переменной readcount), должен разблокировать ресурс, тем самым сделав его доступным для писателей.

В этом решении каждый писатель должен запрашивать ресурс индивидуально. Это означает, что поток читателей может впоследствии заблокировать всех потенциальных писателей и морить их голодом. Это так, потому что после того, как первый считыватель заблокирует ресурс, ни один писатель не может заблокировать его до того, как он будет освобожден. И он будет выпущен только последним читателем. Следовательно, это решение не удовлетворяет справедливости.

Проблема вторых читателей – писателей

Первое решение неоптимально, потому что читатель может р1 мог бы иметь замок, писатель W ждать блокировки, а затем читатель р2 запрашивает доступ. Было бы несправедливо р2 немедленно прыгнуть вперед W; если это случалось достаточно часто, W бы голодать. Вместо, W следует начать как можно скорее. Это мотивация для проблема вторых читателей – писателей, в котором добавлено ограничение, что ни один писатель, однажды добавленный в очередь, не должен ждать дольше, чем это абсолютно необходимо. Это также называется писатели-предпочтения.

Решение сценария предпочтений писателей:[1]

 1 int счетчик, writecount;                   // (начальное значение = 0) 2 семафор rmutex, wmutex, читать, ресурс; // (начальное значение = 1) 3  4 // ЧИТАТЕЛЬ 5 читатель() { 6 <ВХОД Раздел> 7   читать.п();                 // Указывает, что читатель пытается войти 8   rmutex.п();                  // блокировка раздела ввода, чтобы избежать состояния гонки с другими читателями 9   счетчик++;                 // сообщаем о себе как о читателе10   если (счетчик == 1)          // проверяет, являетесь ли вы первым читателем11     ресурс.п();              // если вы первый читатель, заблокируем ресурс12   rmutex.V();                  // выпускаем раздел записи для других читателей13   читать.V();                 // указывает, что вы закончили попытки доступа к ресурсу14 15 <КРИТИЧЕСКИЙ Раздел>16 // чтение выполняется17 18 <ВЫХОД Раздел>19   rmutex.п();                  // резервный раздел выхода - избегает состояния гонки с читателями20   счетчик--;                 // указываем, что вы уходите21   если (счетчик == 0)          // проверяет, уходят ли вы последним читателем22     ресурс.V();              // если последний, вы должны освободить заблокированный ресурс23   rmutex.V();                  // освобождаем раздел выхода для других читателей24 }25 26 // ПИСАТЕЛЬ27 писатель() {28 <ВХОД Раздел>29   wmutex.п();                  // зарезервировать раздел записи для писателей - избегает условий гонки30   writecount++;                // сообщаем о себе как о писателе, вводящем31   если (writecount == 1)         // проверяет, первый ли вы писатель32     читать.п();               // если вы первый, то вы должны заблокировать читателей. Не позволяйте им войти в CS33   wmutex.V();                  // освобождаем раздел ввода34   ресурс.п();                // резервируем ресурс для себя - предотвращает одновременное редактирование общего ресурса другими авторами35 <КРИТИЧЕСКИЙ Раздел>36   // запись выполняется37   ресурс.V();                // выпускаем файл38 39 <ВЫХОД Раздел>40   wmutex.п();                  // резервный выход из секции41   writecount--;                // указать, что вы уходите42   если (writecount == 0)         // проверяет, последний ли вы писатель43     читать.V();               // если вы последний писатель, вы должны разблокировать читателей. Позволяет им попробовать войти в CS для чтения44   wmutex.V();                  // освобождаем раздел выхода45 }

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

Ни один читатель не может войти в раздел ввода, если семафор попытки чтения был установлен писателем ранее. Читатель должен дождаться, пока последний писатель разблокирует ресурс и произведет чтение семафоров. С другой стороны, если конкретный считыватель заблокировал семафор попытки чтения, это будет указывать любому потенциальному параллельному записывающему устройству, что в секции ввода есть считыватель. Таким образом, писатель будет ждать, пока читатель выпустит повторную попытку, а затем писатель немедленно заблокирует его для себя и всех последующих писателей. Однако писатель не сможет получить доступ к ресурсу, пока текущий читатель не освободит ресурс, что происходит только после того, как читатель завершит работу с ресурсом в критической секции.

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

Если нет писателей, желающих получить доступ к ресурсу, на что указывает читателю статус семафора чтения, то читатели не будут блокировать ресурс. Это сделано для того, чтобы писатель мог немедленно взять под контроль ресурс, как только текущий читатель закончит чтение. В противном случае писатель должен будет дождаться завершения очереди считывателей, прежде чем последний сможет разблокировать семафор чтения. Как только появляется писатель, он попытается установить повторную попытку и повесит трубку, ожидая, пока текущий читатель освободит повторную попытку. Затем он получит контроль над ресурсом, как только текущий читатель закончит чтение, и заблокирует всех будущих читателей. Все последующие считыватели будут зависать от семафора readtry, ожидая, пока писатели закончат работу с ресурсом и откроют ворота, отпустив readtry.

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

Проблема третьих читателей – писателей

Фактически, решения, подразумеваемые обеими формулировками проблемы, могут привести к голодной смерти - первая может голодать писателей в очереди, а вторая может голодать читателей. Следовательно третья проблема читателей – писателей иногда предлагается, что добавляет ограничение, что ни одна нить не должна голодать; то есть операция получения блокировки совместно используемых данных всегда будет завершаться в течение ограниченного периода времени. Справедливое решение как для читателей, так и для писателей может быть следующим:

 1 int счетчик;                // инициализация до 0; количество читателей, которые в данный момент обращаются к ресурсу 2  3 // все семафоры инициализируются 1 4 семафор ресурс;           // контролирует доступ (чтение / запись) к ресурсу 5 семафор rmutex;             // для синхронизации изменений общей переменной readcount 6 семафор serviceQueue;       // СПРАВЕДЛИВОСТЬ: сохраняет порядок запросов (сигнализация должна быть FIFO) 7  8 // ЧИТАТЕЛЬ 9 читатель() {10 <ВХОД Раздел>11   serviceQueue.п();           // ждем очереди на обслуживание12   rmutex.п();                 // запрашиваем монопольный доступ к счетчику чтения13   счетчик++;                // обновляем счетчик активных читателей14   если (счетчик == 1)         // если я первый читатель15     ресурс.п();             // запрашиваем доступ к ресурсам для читателей (писатели заблокированы)16   serviceQueue.V();           // пусть обслуживается следующий в очереди17   rmutex.V();                 // освобождаем доступ к счетчику чтения18     19 <КРИТИЧЕСКИЙ Раздел>20 // чтение выполняется21     22 <ВЫХОД Раздел>23   rmutex.п();                 // запрашиваем монопольный доступ к счетчику чтения24   счетчик--;                // обновляем счетчик активных читателей25   если (счетчик == 0)         // если читателей не осталось26     ресурс.V();             // освобождаем доступ к ресурсам для всех27   rmutex.V();                 // освобождаем доступ к счетчику чтения28 }29 30 // ПИСАТЕЛЬ31 писатель() {32 <ВХОД Раздел>33   serviceQueue.п();           // ждем очереди на обслуживание34   ресурс.п();               // запрашиваем монопольный доступ к ресурсу35   serviceQueue.V();           // пусть обслуживается следующий в очереди36     37 <КРИТИЧЕСКИЙ Раздел>38 // запись выполняется39     40 <ВЫХОД Раздел>41   ресурс.V();               // освободить доступ к ресурсам для следующего читателя / писателя42 }

Это решение может удовлетворять только условию «ни одному потоку не может быть позволено голодать» тогда и только тогда, когда семафоры сохраняют порядок «первым пришел - первым вышел» при блокировании и освобождении потоков. В противном случае, например, заблокированный модуль записи может оставаться заблокированным на неопределенное время, а цикл других модулей записи уменьшает семафор до этого.

Простейшая задача читателя и писателя

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

Обратите внимание, что это решение становится проще, чем в общем случае, потому что оно эквивалентно Ограниченный буфер проблема, а значит только N читателям разрешен вход параллельно, N размер буфера.

Читатель

делать {    ждать(читать)    ............    чтение данные    ............    сигнал(записывать)} пока (ИСТИННЫЙ);

Писатель

делать {    ждать(записывать)    .............    письмо данные    .............    сигнал(читать)} пока (ИСТИННЫЙ);

Алгоритм

  1. Reader будет запускаться после Writer из-за семафора чтения.
  2. Writer прекратит запись, когда семафор записи достигнет 0.
  3. Читатель прекратит чтение, когда семафор чтения достигнет 0.

В Writer значение семафора записи дается для семафора чтения, а в модуле чтения значение чтения дается для записи по завершении цикла.

Смотрите также

Рекомендации

  1. ^ а б Куртуа, П. Дж .; Heymans, F .; Парнас, Д. Л. (1971). «Параллельное управление с« читателями »и« писателями »"" (PDF). Коммуникации ACM. Дои:10.1145/362759.362813.
  2. ^ Таубенфельд, Гади (2006). Алгоритмы синхронизации и параллельное программирование. Pearson Education. п. 301.
  • Моррис Дж. М. (1979). Безголодное решение проблемы взаимного исключения. Inf Process Lett 8: 76–80
  • Справедливое решение проблемы читателя-писателя только с помощью семафоров. Х. Баллхаузен, 2003 г. arXiv:cs / 0303005
  • Более быстрое справедливое решение проблемы читателя – писателя. В. Попов, О. Мазонка 2013 arXiv:1309.4507

внешняя ссылка