Спецификация модели памяти в pthreads
Существуют ли гарантии того, что запись в память в одном потоке станет видимой в других потоках, используя pthread
s?
По сравнению с Java, спецификация языка Java имеет раздел, который определяет взаимодействие блокировок и памяти, что позволяет писать переносимый многопоточный код Java.
Есть ли соответствующая спецификация pthreads?
Конечно, вы всегда можете пойти и сделать общие данные нестабильными, но это не то, что мне нужно.
Если это зависит от платформы, существует ли стандарт де-факто? Или следует использовать другую библиотеку потоков?
2 ответа
POSIX определяет модель памяти в 4.11 Синхронизация памяти:
Приложения должны гарантировать, что доступ к любой ячейке памяти более чем одним потоком управления (потоками или процессами) ограничен, так что ни один поток управления не может читать или изменять ячейку памяти, в то время как другой поток управления может изменять ее. Такой доступ ограничен использованием функций, которые синхронизируют выполнение потока, а также синхронизируют память относительно других потоков. Следующие функции синхронизируют память по отношению к другим потокам:
- вилка ()
- pthread_barrier_wait ()
- pthread_cond_broadcast ()
- pthread_cond_signal ()
- pthread_cond_timedwait ()
- pthread_cond_wait ()
- pthread_create ()
- pthread_join ()
- pthread_mutex_lock()
- pthread_mutex_timedlock ()
- pthread_mutex_trylock ()
- pthread_mutex_unlock()
- pthread_spin_lock ()
- pthread_spin_trylock ()
- pthread_spin_unlock ()
- pthread_rwlock_rdlock ()
- pthread_rwlock_timedrdlock ()
- pthread_rwlock_timedwrlock ()
- pthread_rwlock_tryrdlock ()
- pthread_rwlock_trywrlock ()
- pthread_rwlock_unlock ()
- pthread_rwlock_wrlock ()
- sem_post ()
- sem_timedwait ()
- sem_trywait ()
- sem_wait ()
- semctl ()
- semop ()
- Подождите()
- waitpid ()
Функция pthread_once () должна синхронизировать память для первого вызова в каждом потоке для данного объекта pthread_once_t.
Функция pthread_mutex_lock() не должна синхронизировать память, если тип мьютекса, если PTHREAD_MUTEX_RECURSIVE и вызывающий поток уже владеет мьютексом. Функция pthread_mutex_unlock() не должна синхронизировать память, если тип мьютекса - PTHREAD_MUTEX_RECURSIVE, а мьютекс имеет число блокировок больше единицы.
Если явно не указано иное, если одна из вышеперечисленных функций возвращает ошибку, не определено, вызывает ли вызов синхронизацию памяти.
Приложения могут позволять более чем одному потоку управления одновременно считывать область памяти.
Я не знаю, что потоки POSIX дают такие гарантии. У них нет модели атомарного доступа к объектам с общим потоком. Если бы это было для потоков POSIX, единственной гарантией, которую вы можете иметь для видимости изменений, является использование какой-то блокировки.
Современный C, C11 (и, вероятно, также C++11) имеет модель для такого рода вопросов. У него есть потоки и атомы (заборы и все такое), которые дают вам точные правила, когда вы можете предположить, что изменение, выполненное одним потоком, видимо другим.
Интерфейс потоков C11 является подготовленной версией потоков POSIX с меньшими функциональными возможностями. К сожалению, спецификация семантики этого потокового интерфейса пока еще слишком мала, в основном семантика отсутствует во многих местах. Но комбинация интерфейсов C11 и семантики потоков POSIX может дать вам хорошее представление о том, как все работает в современных системах.
Редактирование: поэтому, если вы хотите иметь гарантии для синхронизации памяти, используйте либо интерфейсы блокировки, которые предоставляет POSIX, либо переходите к атомарным операциям. Все современные компиляторы имеют расширения, обеспечивающие их, gcc и family (icc, opencc, clang) имеют, например, серию __sync...
внутренние команды. Clang это его новейшая версия также уже имеет поддержку нового C11 _Atomic
особенность. Также доступны обертки, которые дают вам интерфейсы для других компиляторов, приближающихся к _Atomic
,