Безопасность потоков в словаре Python
У меня есть класс, который содержит словарь
class OrderBook:
orders = {'Restaurant1': None,
'Restaurant2': None,
'Restaurant3': None,
'Restaurant4': None}
@staticmethod
def addOrder(restaurant_name, orders):
OrderBook.orders[restaurant_name] = orders
И я запускаю 4 потока (по одному для каждого ресторана), которые вызывают метод OrderBook.addOrder
. Вот функция, выполняемая каждым потоком:
def addOrders(restaurant_name):
#creates orders
...
OrderBook.addOrder(restaurant_name, orders)
Это безопасно, или мне нужно использовать блокировку перед вызовом addOrder
?
Ответы
Ответ 1
Встроенные в Python структуры поточно-безопасны для отдельных операций, но иногда бывает трудно увидеть, где оператор действительно становится несколькими операциями.
Ваш код должен быть безопасным. Имейте в виду: замок здесь добавит почти никаких накладных расходов и даст вам душевное спокойствие.
http://effbot.org/pyfaq/what-kinds-of-global-value-mutation-are-thread-safe.htm содержит более подробную информацию.
Ответ 2
Да, встроенные типы по сути являются потокобезопасными:
http://docs.python.org/glossary.html#term-global-interpreter-lock
Это упрощает реализацию CPython, делая объектную модель (включая критические встроенные типы, такие как dict), неявно защищенной от одновременного доступа.
Ответ 3
Стоит отметить, что руководство по стилю Google не советует полагаться на атомарность, как я объяснил более подробно в разделе: Является ли назначение переменных Python атомарным?
Не полагайтесь на атомарность встроенных типов.
Хотя встроенные типы данных Pythons, такие как словари, по-видимому, имеют атомарные операции, есть угловые случаи, когда они не являются атомарными (например, если __hash__
или __eq__
реализованы как методы Python), и на их атомарность не следует полагаться. Также не следует полагаться на атомарное присвоение переменных (поскольку это, в свою очередь, зависит от словарей).
Используйте тип данных Queue
module Queue в качестве предпочтительного способа передачи данных между потоками. В противном случае используйте модуль потоков и его блокирующие примитивы. Узнайте о правильном использовании условных переменных, чтобы вы могли использовать threading.Condition
вместо использования низкоуровневых блокировок.
И я согласен с этим: в CPython уже есть GIL, поэтому снижение производительности при использовании Lock будет незначительным. Гораздо дороже будут потраченные часы на поиск ошибок в сложной кодовой базе, когда эти детали реализации CPython изменятся за один день.