Является ли назначение переменных Python атомарным? - PullRequest
25 голосов
/ 18 февраля 2010

Допустим, я использую обработчик signal для обработки интервального таймера.

def _aHandler(signum, _):
  global SomeGlobalVariable
  SomeGlobalVariable=True

Могу ли я установить SomeGlobalVariable, не беспокоясь о том, что в маловероятном сценарии, когда при настройке SomeGlobalVariable (т. Е. Виртуальная машина Python выполняла байт-код для установки переменной), что назначение в обработчике сигнала что-то сломает? (то есть метастабильное состояние)

Обновление : меня особенно интересует случай, когда "составное присваивание" выполняется вне обработчика.

(может быть, я думаю, что слишком низкий уровень, и об этом все заботятся в Python ... исходя из опыта встраиваемых систем, у меня время от времени появляются такие импульсы)

Ответы [ 3 ]

19 голосов
/ 18 февраля 2010

Простое присваивание простым переменным является «атомарным» AKA-потокобезопасным (составные присваивания, такие как +=, или присваивания элементам или атрибутам объектов не обязательны, но ваш пример - простое присвоение простой, хотя и глобальной, переменной, таким образом, безопасно).

2 голосов

Руководство по стилю Google советует против него

Я не утверждаю, что руководства по стилю Google - это абсолютная истина, но обоснование в разделе «Потоки» дает некоторое представление (выделение мое):

Не полагайтесь на атомарность встроенных типов.

Хотя во встроенных типах данных Python, таких как словари, кажется, есть атомарные операции, существуют угловые случаи, когда они не атомарны (например, если __hash__ или __eq__ реализованы как методы Python) и их атомарность не должна полагаться. Также не следует полагаться на назначение атомарных переменных (поскольку это, в свою очередь, зависит от словарей).

Используйте тип данных Queue модуля Queue в качестве предпочтительного способа передачи данных между потоками. В противном случае используйте модуль потоков и его блокирующие примитивы. Узнайте о правильном использовании условных переменных, чтобы вы могли использовать threading.Condition вместо блокировок более низкого уровня.

Итак, моя интерпретация заключается в том, что в Python все похоже на dict, и когда вы делаете a = b в бэкэнде, где-то происходит globals['a'] = b, что плохо, так как dicts не обязательно являются потокобезопасными.

Для одной переменной Queue не идеален, поскольку мы хотим, чтобы он содержал только один элемент, и я не смог найти идеальный ранее существующий контейнер в stdlib, который автоматически синхронизирует метод .set(). Так что сейчас я делаю только:

import threading

myvar = 0
myvar_lock = threading.Lock()
with myvar_lock:
    myvar = 1
with myvar_lock:
    myvar = 2

Интересно, что Мартелли, похоже, не против рекомендации по стилю Google :-) (он работает в Google)

Интересно, имеет ли значение CPYthon GIL этот вопрос: Что такое глобальная блокировка интерпретатора (GIL) в CPython?

Эта ветка также предполагает, что CPython-дикты являются поточно-ориентированными, включая следующую глоссарийную цитату, в которой явно упоминается https://docs.python.org/3/glossary.html#term-global-interpreter-lock

Это упрощает реализацию CPython, делая объектную модель (включая критические встроенные типы, такие как dict) неявно защищенной от одновременного доступа.

1 голос
/ 18 февраля 2010

Составное назначение включает три шага: чтение-обновление-запись. Это условие гонки, если другой поток запускается и записывает новое значение в местоположение после чтения, но до записи. В этом случае устаревшее значение обновляется и записывается обратно, что приведет к сбою любого нового значения, записанного другим потоком. В Python все, что включает в себя выполнение однобайтового кода, ДОЛЖНО быть атомарным, но составное присваивание не соответствует этому критерию. Используйте замок.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...