Безопасность потоков - это концепция компьютерного программирования, применимая к многопоточному коду. Поточно-безопасный код манипулирует только общими структурами данных таким образом, чтобы гарантировать, что все потоки ведут себя должным образом и выполняют свои проектные спецификации без непреднамеренного взаимодействия. Существуют различные стратегии создания потоковобезопасных структур данных. [1] [2]
Программа может выполнять код одновременно в нескольких потоках в общем адресном пространстве, где каждый из этих потоков имеет доступ практически ко всей памяти каждого другого потока. Поточная безопасность - это свойство, которое позволяет коду работать в многопоточных средах путем восстановления некоторых соответствий между фактическим потоком управления и текстом программы посредством синхронизации .
Уровни безопасности потоков
Программные библиотеки могут предоставить определенные гарантии безопасности потоков. Например, параллельное чтение может быть гарантировано потокобезопасным, а одновременная запись - нет. Является ли программа, использующая такую библиотеку, потокобезопасной, зависит от того, использует ли она библиотеку в соответствии с этими гарантиями.
Различные поставщики используют немного разную терминологию для обеспечения безопасности потоков: [3] [4] [5] [6]
- Потокобезопасность : при одновременном доступе нескольких потоков в реализации гарантируется отсутствие условий гонки .
- Условно безопасный : разные потоки могут обращаться к разным объектам одновременно, а доступ к общим данным защищен от состояния гонки.
- Не потокобезопасный : к структурам данных не должны одновременно обращаться разные потоки.
Гарантии безопасности потоков обычно также включают шаги проектирования для предотвращения или ограничения риска различных форм взаимоблокировок , а также оптимизацию для максимизации одновременной производительности. Однако гарантии отсутствия тупиков не всегда могут быть предоставлены, поскольку тупиковые ситуации могут быть вызваны обратными вызовами и нарушением архитектурного разделения независимо от самой библиотеки.
Подходы к реализации
Ниже мы обсудим два класса подходов к предотвращению состояний гонки для обеспечения безопасности потоков.
Первый класс подходов направлен на предотвращение совместного использования состояния и включает:
- Повторный вход
- Написание кода таким образом, чтобы он мог частично выполняться потоком, выполняться тем же потоком или одновременно выполняться другим потоком и при этом правильно завершать исходное выполнение. Это требует сохранения информации о состоянии в переменных, локальных для каждого выполнения, обычно в стеке, а не в статических или глобальных переменных или другом нелокальном состоянии. Все нелокальные состояния должны быть доступны через атомарные операции, и структуры данных также должны быть реентерабельными.
- Локальное хранилище потока
- Переменные локализованы, поэтому каждый поток имеет свою собственную частную копию. Эти переменные сохраняют свои значения в пределах подпрограммы и других границ кода и являются потокобезопасными, поскольку они локальны для каждого потока, даже если код, который обращается к ним, может выполняться одновременно другим потоком.
- Неизменяемые объекты
- Состояние объекта не может быть изменено после постройки. Это означает, что совместно используются только данные только для чтения и обеспечивается внутренняя безопасность потоков. Изменяемые (неконстантные) операции могут быть реализованы таким образом, что они создают новые объекты вместо изменения существующих. Этот подход характерен для функционального программирования, а также используется строковыми реализациями в Java, C # и Python. (См. Неизменяемый объект .)
Второй класс подходов связан с синхронизацией и используется в ситуациях, когда нельзя избежать общего состояния:
- Взаимное исключение
- Доступ к общим данным сериализуется с использованием механизмов, которые гарантируют, что только один поток читает или записывает общие данные в любое время. Включение взаимных потребностей исключения быть хорошо продумано, так как неправильное использование может привести к побочным эффектам , как тупики , livelocks и ресурсный голод .
- Атомарные операции
- Доступ к общим данным осуществляется с помощью атомарных операций, которые не могут быть прерваны другими потоками. Обычно это требует использования специальных инструкций на машинном языке , которые могут быть доступны в библиотеке времени выполнения . Поскольку операции являются атомарными, общие данные всегда сохраняются в допустимом состоянии, независимо от того, как к ним обращаются другие потоки. Атомарные операции составляют основу многих механизмов блокировки потоков и используются для реализации примитивов взаимного исключения.
Примеры
В следующей части Java коде Ключевое слово Java синхронизируются делает метод поточно-:
class Counter { private int i = 0 ; общедоступная синхронизированная void inc () { i ++ ; } }
В языке программирования C у каждого потока есть свой стек. Однако статическая переменная не хранится в стеке; все потоки имеют к нему одновременный доступ. Если несколько потоков перекрываются при выполнении одной и той же функции, возможно, что статическая переменная может быть изменена одним потоком, в то время как другой находится на полпути к ее проверке. Эта сложная для диагностики логическая ошибка , которая может компилироваться и работать правильно большую часть времени, называется состоянием гонки . Один из распространенных способов избежать этого использовать другую общую переменную в качестве «замок» или «мьютекс» (от MUT UAL экс ключения).
В следующем фрагменте кода C функция является поточно-ориентированной, но не реентерабельной:
# включить int increment_counter () { статический счетчик int = 0 ; статический pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER ; // разрешаем только одному потоку увеличиваться за раз pthread_mutex_lock ( & mutex ); ++ счетчик ; // сохраняем значение перед тем, как другие потоки увеличивают его дальше int result = counter ; pthread_mutex_unlock ( & mutex ); вернуть результат ; }
В приведенном выше примере, increment_counter
может вызываться разными потоками без каких-либо проблем, поскольку мьютекс используется для синхронизации всего доступа к общей counter
переменной. Но если функция используется в повторно входимом обработчике прерывания и возникает второе прерывание, когда мьютекс заблокирован, вторая процедура зависнет навсегда. Поскольку обслуживание прерывания может отключить другие прерывания, может пострадать вся система.
Одна и та же функция может быть реализована как поточно-ориентированной, так и повторно входимой, используя безблокировочную атомику в C ++ 11 :
# включить <атомарный>int increment_counter () { статический std :: atomic < int > счетчик ( 0 ); // приращение гарантированно выполняется атомарно int result = ++ counter ; вернуть результат ; }
Смотрите также
Рекомендации
- ^ Керриск, Майкл (2010). Интерфейс программирования Linux . Пресс без крахмала . п. 655.
- ^ «Руководство по многопоточному программированию» . Корпорация Oracle . Ноябрь 2010 г.
Процедура является потокобезопасной, если процедура логически корректна при одновременном выполнении несколькими потоками.
- ^ «Повторная входимость и безопасность потоков | Qt 5.6» . Qt Project . Проверено 20 апреля 2016 .
- ^ "ip :: tcp - 1.51.0" . Boost.org . Проверено 16 октября 2013 .
- ^ «Классификация безопасности потоков API» . Publib.boulder.ibm.com. 1998-06-09 . Проверено 16 октября 2013 .[ мертвая ссылка ]
- ^ «Уровни безопасности интерфейса MT - Руководство по многопоточному программированию» . Docs.oracle.com. 2010-11-01 . Проверено 16 октября 2013 .
Внешние ссылки
- Эксперты по Java Q&A (20 апреля 1999 г.). «Поточно-ориентированный дизайн (20.04.1999)» . JavaWorld.com . Проверено 22 января 2012 .
- TutorialsDesk (30 сентября 2014 г.). «Учебное пособие по синхронизации и безопасности потоков с примерами на Java» . TutorialsDesk.com . Проверено 22 января 2012 .
- Веннерс, Билл (1 августа 1998 г.). «Дизайн для обеспечения безопасности потоков» . JavaWorld.com . Проверено 22 января 2012 .
- Зьюсс, Майкл (15 октября 2006 г.). «Краткое руководство по обеспечению безопасности потоков» . Параллельное мышление . Проверено 22 января 2012 .