Нужен ли семафор при чтении из глобальной структуры? - PullRequest
9 голосов
/ 05 ноября 2008

Довольно простой вопрос, но я не вижу, чтобы он задавался где-либо.

Допустим, у нас есть глобальная структура (в C), например:

struct foo {
  int written_frequently1;
  int read_only;
  int written_frequently2;
};

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

Если мы хотим, чтобы множество потоков читало элемент read_only, а ни один не записывал, нам нужен семафор для доступа к структуре только для чтения?

(Я склонен сказать нет, потому что тот факт, что местоположения непосредственно до и после постоянно меняются, не должен влиять на элемент read_only, а чтение нескольких потоков не должно мешать друг другу. Но Я не уверен.)


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

struct bar {
  char written_frequently1[LONGISH_LEN];
  char read_only[LONGISH_LEN];
  char written_frequently2[LONGISH_LEN];
};

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

Тот факт, что члены были целыми числами, и поэтому записи, вероятно, являются атомарными, в данном случае на самом деле является просто красной сельдью.]

Ответы [ 10 ]

7 голосов
/ 05 ноября 2008

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

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


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

Пример: поток обновляет элементы "день", "месяц" и "год" структуры. Это должно происходить атомарно, чтобы другой поток не читал структуру после приращений «месяц», но до того, как «день» обнуляется до 1, чтобы избежать дат, таких как 31 февраля. Обратите внимание, что при чтении необходимо соблюдать мьютекс ; в противном случае вы можете прочитать ошибочное, частично обновленное значение.

6 голосов
/ 05 ноября 2008

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

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

4 голосов
/ 05 ноября 2008

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

3 голосов
/ 06 ноября 2008

Читателям тоже нужны мьютексы!

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

Вот почему, в виде примера.

Представьте себе часы, которые обновляются каждую секунду с кодом:

if (++seconds > 59) {        // Was the time hh:mm:59?
   seconds = 0;              // Wrap seconds..
   if (++minutes > 59)  {    // ..and increment minutes.  Was it hh:59:59?
     minutes = 0;            // Wrap minutes..
     if (++hours > 23)       // ..and increment hours.  Was it 23:59:59?
        hours = 0;           // Wrap hours.
    }
}

Если код не защищен мьютексом, другой поток может прочитать переменные hours, minutes и seconds во время обновления. После кода выше:

[Start just before midnight] 23:59:59
[WRITER increments seconds]  23:59:60
[WRITER wraps seconds]       23:59:00
[WRITER increments minutes]  23:60:00
[WRITER wraps minutes]       23:00:00
[WRITER increments hours]    24:00:00
[WRITER wraps hours]         00:00:00

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

Исправление простое.

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

2 голосов
/ 05 ноября 2008

номер

В общем случае вам нужны семафоры для предотвращения одновременного доступа к ресурсам (в данном случае int). Однако, поскольку элемент read_only доступен только для чтения, он не будет меняться между / во время доступа. Обратите внимание, что это даже не должно быть атомарное чтение - если ничего не меняется, вы всегда в безопасности.

Как вы устанавливаете read_only изначально?

1 голос
/ 17 марта 2009

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

1 голос
/ 05 ноября 2008

Если все темы только для чтения, вам не нужен семафор.

0 голосов
/ 27 октября 2009

Большое спасибо всем великим ответчикам (и за все великие ответы).

Подводя итог:

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

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

0 голосов
/ 05 ноября 2008

Добавление к предыдущим ответам:

  1. В этом случае естественной парадигмой синхронизации является взаимное исключение, а не семафоры.
  2. Я согласен, что вам не нужен мьютекс для переменных только для чтения.
  3. Если часть структуры для чтения и записи имеет ограничения согласованности, в общем случае вам потребуется один мьютекс для всех из них, чтобы операции были атомарными.
0 голосов
/ 05 ноября 2008

Я бы скрыл каждое поле позади вызова функции. Поля только для записи будут иметь семафор. Только для чтения только возвращает значение.

...