Безопасность резьбы
В многопоточном компьютерном программировании функция является потокобезопасной , если она может быть вызвана или доступна одновременно нескольким потокам, не вызывая неожиданного поведения, условий гонки или повреждения данных. [1] [2] Как и в многопоточном контексте, когда программа одновременно выполняет несколько потоков в общем адресном пространстве каждого другого потока , и каждый из этих потоков имеет доступ ко всей памяти , потокобезопасные функции должны гарантировать, что все эти потоки ведут себя правильно и соответствуют своим проектным спецификациям. без непреднамеренного взаимодействия. [3]
Существуют различные стратегии создания потокобезопасных структур данных. [3]
Уровни потокобезопасности [ править ]
Разные поставщики используют несколько разную терминологию для потокобезопасности. [4] но наиболее часто используемая терминология потокобезопасности: [2]
- Непотокобезопасно : к структурам данных не должны быть доступны одновременно разные потоки.
- Потокобезопасность, сериализация . Используйте один мьютекс для всех ресурсов , чтобы гарантировать, что в потоке не возникнет гонок , когда к этим ресурсам одновременно обращаются несколько потоков.
- Потокобезопасность, MT-безопасность : используйте мьютекс для каждого отдельного ресурса, чтобы гарантировать отсутствие условий гонки в потоке , когда к этим ресурсам одновременно обращаются несколько потоков.
Гарантии потокобезопасности обычно также включают этапы проектирования, направленные на предотвращение или ограничение риска различных форм взаимоблокировок , а также оптимизацию для максимизации одновременной производительности. Однако гарантии отсутствия взаимоблокировок не всегда могут быть предоставлены, поскольку взаимоблокировки могут быть вызваны обратными вызовами и нарушением многоуровневой архитектуры, независимой от самой библиотеки.
Библиотеки программного обеспечения могут предоставить определенные гарантии потокобезопасности. [5] Например, параллельное чтение может быть гарантированно потокобезопасным, а параллельная запись — нет. Является ли программа, использующая такую библиотеку, потокобезопасной, зависит от того, использует ли она библиотеку в соответствии с этими гарантиями.
Подходы к реализации [ править ]
Ниже мы обсудим два класса подходов к предотвращению условий гонки для достижения потокобезопасности.
Первый класс подходов направлен на избежание общего состояния и включает в себя:
- Повторный вход [6]
- Написание кода таким образом, чтобы он мог частично выполняться потоком, выполняться тем же потоком или одновременно выполняться другим потоком и при этом правильно завершать исходное выполнение. Это требует сохранения информации о состоянии в переменных, локальных для каждого выполнения, обычно в стеке, а не в статических или глобальных переменных или другом нелокальном состоянии. Доступ ко всем нелокальным состояниям должен осуществляться посредством атомарных операций, а структуры данных также должны быть реентерабельными.
- Локальное хранилище потока
- Переменные локализуются таким образом, что каждый поток имеет свою собственную копию. Эти переменные сохраняют свои значения вне границ подпрограммы и другого кода и являются потокобезопасными, поскольку они локальны для каждого потока, даже если код, который обращается к ним, может выполняться одновременно другим потоком.
- Неизменяемые объекты
- Состояние объекта не может быть изменено после постройки. Это означает, что совместно используются только данные, доступные только для чтения, и достигается присущая потокобезопасность. Изменяемые (неконстантные) операции могут быть реализованы таким образом, что они создают новые объекты вместо изменения существующих. Этот подход характерен для функционального программирования , а также используется в строковых реализациях на Java, C# и Python. (См. Неизменяемый объект .)
Второй класс подходов связан с синхронизацией и используется в ситуациях, когда невозможно избежать общего состояния:
- Взаимное исключение
- Доступ к общим данным сериализуется с использованием механизмов, которые гарантируют, что только один поток читает или записывает общие данные в любой момент времени. Включение взаимного исключения должно быть хорошо продумано, поскольку неправильное использование может привести к побочным эффектам, таким как взаимоблокировки , активные блокировки и нехватка ресурсов .
- Атомарные операции
- Доступ к общим данным осуществляется с помощью атомарных операций, которые не могут быть прерваны другими потоками. Обычно для этого требуется использование специальных инструкций машинного языка , которые могут быть доступны в библиотеке времени выполнения . Поскольку операции являются атомарными, общие данные всегда сохраняются в допустимом состоянии, независимо от того, как к ним обращаются другие потоки. Атомарные операции составляют основу многих механизмов блокировки потоков и используются для реализации примитивов взаимного исключения.
Примеры [ править ]
В следующем фрагменте кода Java ключевое слово Java Synchronized делает метод потокобезопасным:
class Counter {
private int i = 0;
public synchronized void inc() {
i++;
}
}
В языке программирования C каждый поток имеет свой стек. Однако статическая переменная не сохраняется в стеке; все потоки имеют одновременный доступ к нему. Если несколько потоков перекрываются при выполнении одной и той же функции, возможно, что статическая переменная может быть изменена одним потоком, пока другой находится на полпути ее проверки. Эта трудно диагностируемая логическая ошибка , которая большую часть времени компилируется и работает правильно, называется состоянием гонки . Один из распространенных способов избежать этого — использовать другую общую переменную в качестве «блокировки» или «мьютекса» (от исключения взаимного ) .
В следующем фрагменте кода C функция является потокобезопасной, но не реентерабельной:
# include <pthread.h>
int increment_counter ()
{
static int counter = 0;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// only allow one thread to increment at a time
pthread_mutex_lock(&mutex);
++counter;
// store value before any other threads increment it further
int result = counter;
pthread_mutex_unlock(&mutex);
return result;
}
В приведенном выше increment_counter
могут без проблем вызываться разными потоками, поскольку для синхронизации всего доступа к общему ресурсу используется мьютекс. counter
переменная. Но если функция используется в обработчике реентерабельного прерывания и возникает второе прерывание, пока мьютекс заблокирован, вторая подпрограмма зависнет навсегда. Поскольку обслуживание прерываний может отключить другие прерывания, может пострадать вся система.
Одна и та же функция может быть реализована как потокобезопасная, так и реентерабельная, используя атомарность без блокировок в C++11 :
# include <atomic>
int increment_counter ()
{
static std::atomic<int> counter(0);
// increment is guaranteed to be done atomically
int result = ++counter;
return result;
}
См. также [ править ]
Ссылки [ править ]
- ^ Керриск, Майкл (2010). Программный интерфейс Linux . Пресс без крахмала . п. 699, «Глава 31: ПОТОКИ: ПОТОКОВАЯ БЕЗОПАСНОСТЬ И ПОНИТОВОЕ ХРАНЕНИЕ»
{{cite book}}
: CS1 maint: постскриптум ( ссылка ) - ^ Перейти обратно: а б Оракул (01.11.2010). «Oracle: потокобезопасность» . Docs.oracle.com . Проверено 16 октября 2013 г. «Процедура является потокобезопасной, если она логически корректна при одновременном выполнении несколькими потоками»; «3 уровень потокобезопасности»
{{cite web}}
: CS1 maint: постскриптум ( ссылка ) - ^ Перейти обратно: а б Оракул (ноябрь 2020 г.). «Руководство по многопоточному программированию: Глава 7. Безопасные и небезопасные интерфейсы» . Документы Оракл . Проверено 30 апреля 2024 г .; «Потокобезопасность»
{{cite web}}
: CS1 maint: постскриптум ( ссылка ) - ^ «Классификация потокобезопасности API» . ИБМ. 11 апреля 2023 г. Проверено 9 октября 2023 г.
- ^ «Уровни безопасности MT для библиотек» . Документы Оракл . Проверено 17 мая 2024 г.
- ^ «Реентерабельность и потокобезопасность | Qt 5.6» . Qt-проект . Проверено 20 апреля 2016 г.
Внешние ссылки [ править ]
- Эксперты по Java, вопросы и ответы (20 апреля 1999 г.). «Потокобезопасная конструкция (20.04.99)» . JavaWorld.com . Проверено 22 января 2012 г.
- TutorialsDesk (30 сентября 2014 г.). «Учебное пособие по синхронизации и потокобезопасности с примерами на Java» . TutorialsDesk.com . Проверено 22 января 2012 г.
- Веннерс, Билл (1 августа 1998 г.). «Проектирование с учетом потокобезопасности» . JavaWorld.com . Проверено 22 января 2012 г.
- Зюсс, Майкл (15 октября 2006 г.). «Краткое руководство по освоению потокобезопасности» . Мышление параллельно . Проверено 22 января 2012 г.