В многопоточном программировании функция является потокобезопасной , когда она может быть вызвана или доступна одновременно несколькими потоками без возникновения непредвиденного поведения, состояний гонки или повреждения данных. [1] [2] Как и в многопоточном контексте, где программа выполняет несколько потоков одновременно в общем адресном пространстве , и каждый из этих потоков имеет доступ к памяти каждого другого потока , потокобезопасные функции должны гарантировать, что все эти потоки ведут себя правильно и выполняют свои проектные спецификации без непреднамеренного взаимодействия. [3]
Существуют различные стратегии создания потокобезопасных структур данных. [3]
Разные поставщики используют немного различную терминологию для потокобезопасности, [4] но наиболее часто используемая терминология потокобезопасности следующая: [2]
Гарантии безопасности потоков обычно также включают шаги по проектированию для предотвращения или ограничения риска различных форм взаимоблокировок , а также оптимизации для максимизации параллельной производительности. Однако гарантии отсутствия взаимоблокировок не всегда могут быть даны, поскольку взаимоблокировки могут быть вызваны обратными вызовами и нарушением архитектурного расслоения независимо от самой библиотеки.
Библиотеки программного обеспечения могут предоставлять определенные гарантии потокобезопасности. [5] Например, параллельные чтения могут быть гарантированно потокобезопасными, но параллельные записи — нет. Является ли программа, использующая такую библиотеку, потокобезопасной, зависит от того, использует ли она библиотеку способом, соответствующим этим гарантиям.
Ниже мы рассмотрим два класса подходов, позволяющих избежать состояний гонки и обеспечить потокобезопасность.
Первый класс подходов направлен на избежание общего состояния и включает в себя:
Второй класс подходов связан с синхронизацией и используется в ситуациях, когда невозможно избежать общего состояния:
В следующем фрагменте кода Java ключевое слово Java synchronized делает метод потокобезопасным:
класс Счетчик { частный int i = 0 ; публичный синхронизированный void inc () { i ++ ; } }
В языке программирования C каждый поток имеет свой собственный стек. Однако статическая переменная не хранится в стеке; все потоки имеют к ней одновременный доступ. Если несколько потоков перекрываются при выполнении одной и той же функции, возможно, что статическая переменная может быть изменена одним потоком, пока другой находится на полпути к ее проверке. Эта трудно диагностируемая логическая ошибка , которая может компилироваться и работать правильно большую часть времени, называется состоянием гонки . Один из распространенных способов избежать этого — использовать другую общую переменную в качестве «блокировки» или «мьютекса» (от mut ual ex clusion).
В следующем фрагменте кода на языке C функция потокобезопасна, но не реентерабельна:
# включить <pthread.h> int increment_counter () { static int counter = 0 ; static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER ; // разрешить увеличивать только один поток за раз pthread_mutex_lock ( & mutex ); ++ счетчик ; // сохраняем значение до того, как любые другие потоки увеличат его еще больше int result = counter ; pthread_mutex_unlock ( & мьютекс ); вернуть результат ; }
В приведенном выше примере increment_counter
может вызываться разными потоками без каких-либо проблем, поскольку мьютекс используется для синхронизации всего доступа к общей counter
переменной. Но если функция используется в реентерабельном обработчике прерываний и возникает второе прерывание, пока мьютекс заблокирован, вторая процедура зависнет навсегда. Поскольку обслуживание прерываний может отключить другие прерывания, может пострадать вся система.
Одну и ту же функцию можно реализовать как потокобезопасную и реентерабельную, используя атомарные методы без блокировок в C++11 :
# включить <атомарный> int increment_counter () { static std :: atomic < int > counter ( 0 ); // приращение гарантированно выполняется атомарно int result = ++ counter ; вернуть результат ; }
{{cite book}}
: CS1 maint: postscript (link){{cite web}}
: CS1 maint: postscript (link){{cite web}}
: CS1 maint: postscript (link)