Видимость памяти через библиотеку pthread?
Я читаю Программирование с помощью POSIX Threads (автор David Butenhof), и он упоминает, используя библиотеку pthread:
Независимо от того, какие значения памяти может видеть поток, когда он разблокирует мьютекс, либо напрямую, либо ожидая условную переменную, он также может быть замечен любым потоком, который позднее блокирует тот же мьютекс. Опять же, данные, записанные после того, как мьютекс разблокирован, не обязательно могут быть замечены потоком, который блокирует мьютекс, даже если запись происходит до блокировки.
Внезапно я задаюсь вопросом: допустим ли следующий код:
Тема А:
strcpy(buffer, "hello world");
pthread_spin_lock(&lock); // assuming the mutex in the statement above can be interchanged with spinlock. I don't see why it can't
pthread_spin_unlock(&lock);
Нить Б:
pthread_spin_lock(&lock);
pthread_spin_unlock(&lock);
// read buffer; assuming thread B has a copy of the pointer somehow
Мой вопрос: может ли нить B увидеть "привет мир" в буфере? Исходя из его заявления, так и должно быть. Я понимаю, что "обычным" способом является защита общего "ресурса" с помощью блокировки. Но давайте предположим, что strcpy() происходит в произвольное время и может происходить только один раз за время существования программы, и давайте предположим, что поток B каким-то образом вызывает pthread_spin_lock() после того, как поток A вызывает pthread_spin_unlock():)
Боковой вопрос: есть ли более быстрый способ сделать изменения в буфере видимыми для других потоков? Скажем, мобильность не проблема, и я нахожусь на CentOS. Одна альтернатива, о которой я могу подумать, - это использование mmap(), но я не уверен, является ли какое-либо глобальное изменение видимым без использования библиотеки pthread.
1 ответ
Я не думаю, что вы поняли это правильно: блокировки магически не передают данные, они являются формой связи между потоками, которая позволяет безопасно перемещать данные.
В своем вопросе вы делаете много предположений. На самом деле все, что вы пишете, в равной степени верно, если бы блокировок не было, если все ваши предположения верны. Проблема с параллельным программированием заключается в том, что в общем случае вы не можете делать подобные предположения.
Если поток A вносит изменения в память, он сразу становится видимым для потока B (с учетом капризов кеширования и оптимизации компилятора). Это всегда так, с замками или без них. Но без блокировок нет никаких гарантий, что запись завершена или даже началась.
Ваша первая цитата предполагает (требует), что вы пишете только в общие данные с установленной блокировкой. Последняя часть рассказывает, какие плохие вещи могут случиться, если вы попытаетесь написать без блокировки в первую очередь.
Я не уверен, что точно означает "даже если запись происходит до блокировки", но это, вероятно, относится к различным состояниям гонки и эффектам кэширования памяти, от которых страдают параллельные программы. Блокировки могут фактически не передавать данные, но они будут закодированы так, что они заставят компилятор синхронизировать память через вызов ("барьер памяти").