Изменение словаря Python из разных потоков - PullRequest
13 голосов
/ 28 декабря 2010

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

То же самое относится к словарям? Или словарь - это набор переменных?

Если бы каждый поток блокировал словарь, это значительно замедлило бы программу, в то время как каждому потоку нужен был бы доступ только для записи к своему маленькому фрагменту словаря.

Если это невозможно, есть ли переменная переменная в python, как в php?

Ответы [ 3 ]

23 голосов
/ 28 декабря 2010

Применимо ли это к словарям?Или словарь - это набор переменных?

Давайте будем более общими:

Что означает «атомарная операция»?

От Wikipedia :

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

Что это значит в Python?

Это означает, что каждая инструкция байт-кода является атомарной (по крайней мере,для Python <3.2, до нового GIL). </p>

Почему это ???

Поскольку Python (CPython) использует Global Interpreter Lock (GIL)) .Интерпретатор CPython использует блокировку, чтобы убедиться, что в интерпретаторе одновременно работает только один поток, и использует «интервал проверки» (см. sys.getcheckinterval()), чтобы узнать, сколько инструкций байт-кода нужно выполнить перед переключениеммежду потоками (по умолчанию установлено 100).

Итак, что же это значит ??

Это означает, что операции, которые могут быть представлены только одной инструкцией байт-кода,атомное.Например, приращение переменной составляет , а не атомарный, потому что операция выполняется в трех инструкциях байт-кода:

>>> import dis

>>> def f(a):
        a += 1

>>> dis.dis(f)
  2           0 LOAD_FAST                0 (a)
              3 LOAD_CONST               1 (1)      <<<<<<<<<<<< Operation 1 Load
              6 INPLACE_ADD                         <<<<<<<<<<<< Operation 2 iadd
              7 STORE_FAST               0 (a)      <<<<<<<<<<<< Operation 3 store
             10 LOAD_CONST               0 (None)
             13 RETURN_VALUE        

Так что насчет словарей ??

Некоторые операции являются атомарными;например, эта операция является атомарной:

d[x] = y
d.update(d2)
d.keys()

Убедитесь сами:

>>> def f(d):
        x = 1
        y = 1
        d[x] = y

>>> dis.dis(f)
  2           0 LOAD_CONST               1 (1)
              3 STORE_FAST               1 (x)

  3           6 LOAD_CONST               1 (1)
              9 STORE_FAST               2 (y)

  4          12 LOAD_FAST                2 (y)
             15 LOAD_FAST                0 (d)
             18 LOAD_FAST                1 (x)
             21 STORE_SUBSCR                      <<<<<<<<<<< One operation 
             22 LOAD_CONST               0 (None)
             25 RETURN_VALUE   

Смотрите этот , чтобы понять, что делает STORE_SUBSCR.

Но, как вы видите, это не совсем так, потому что эта операция:

             ...
  4          12 LOAD_FAST                2 (y)
             15 LOAD_FAST                0 (d)
             18 LOAD_FAST                1 (x)
             ...

может сделать всю операцию не атомарной.Зачем?Допустим, переменная x также может быть изменена другим потоком ... или что вы хотите, чтобы другой поток очистил ваш словарь ... мы можем назвать много случаев, когда она может пойти не так, поэтому это сложно!И поэтому здесь мы будем применять Закон Мерфи : «Все, что может пойти не так, пойдет не так».

И что теперь?

ЕслиВы все еще хотите разделить переменные между потоками, используйте блокировку:

import threading

mylock = threading.RLock()

def atomic_operation():
    with mylock:
        print "operation are now atomic"
7 голосов
/ 28 декабря 2010

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

  • поток N получает данные из источника (некоторое место в памяти или на диске - переменная, слот всловарь, файл, почти что-либо изменяемое)
  • поток M получает данные из источника
  • поток N изменяет данные
  • поток M изменяет данные
  • поток N перезаписывает источник с измененными данными
  • поток M перезаписывает источник с измененными данными
  • Результат: изменения потока N потеряны / новое общее значение не учитывает изменения потока N с учетом

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

0 голосов
/ 28 декабря 2010

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

Чтобы сделать доступ к исходной структуре похожим (shared[id] = value), потребуется еще немного работы, но не так уж много.

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