Является ли следующий код потокобезопасным в Python? - PullRequest
0 голосов
/ 24 мая 2018

У меня есть код, подобный следующему в модуле Python:

_x = None

def set_x():
 global _x
 _x = # some value (will always be the same)

def worker():
 if _x is None:
  set_x()

Могут ли потоки, созданные на клиенте этого модуля, безопасно вызывать worker ()?

X используется врежим только для чтения в другом месте модуля;после установки он никогда не изменяется.

Да, я понимаю, что x может быть инкапсулирован в класс, и потоки могут создавать свои собственные экземпляры этого класса в стеке и, таким образом, получать безопасность потока, но предположим, что дляВ целях этого вопроса, реализация на основе классов неосуществима или нежелательна (например, модуль представляет собой Singleton, где стоимость установки x относительно высока, и мы хотим устанавливать ее так редко, как это практически возможно).

1 Ответ

0 голосов
/ 24 мая 2018

Предполагается, что вы используете глобальную переменную в set_x:

x = None

def set_x():
    global x
    x = "foo"

def worker():
    if not x:
        set_x()

Существует условие гонки между if not x и фактической установкой x.Таким образом, несколько потоков могут присвоить значение x.Это в основном безвредно для неизменяемых объектов, но проблематично для изменяемых объектов, таких как списки, где переназначение теряет значения в существующем x.И "главным образом" даже объекты, чьи значения сравниваются, могут иметь разные идентификаторы, поэтому == может работать, но is может не работать:

>>> a = "foo"
>>> b = "bar"
>>> c = a + b
>>> d = a + b
>>> c == d
True
>>> c is d
False

И, конечно, пути кода, которые не воспроизводят *Игра 1013 * может обнаружить, что x изменяется от None до ожидаемого значения, пока они выполняются.

Тем более, что x является дорогостоящим для вычисления, хорошим подходом является скрыть его за получателем.Теперь все должны играть в одну и ту же игру, и только один из потоков на самом деле сделает эту работу.

_x = None
_x_lock = threading.Lock()

def get_x():
    global _x
    if _x is None:
        with _x_lock:
            if _x is None:
                _x = "foobar"
    return _x
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...