Изменение словаря Python из разных потоков

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

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

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

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

Ответы

Ответ 1

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

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

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

Ответ 2

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

Пусть будет более общим:

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

От Wikipedia:

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

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

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

Почему это?

Поскольку 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"

Ответ 3

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

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