Как открыть общий доступ к глобальной переменной с резьбой?
Мой пример кода:
from threading import Thread
import time
a = 0 #global variable
def thread1(threadname):
#read variable "a" modify by thread 2
def thread2(threadname):
while 1:
a += 1
time.sleep(1)
thread1 = Thread( target=thread1, args=("Thread-1", ) )
thread2 = Thread( target=thread2, args=("Thread-2", ) )
thread1.join()
thread2.join()
Я не'т знаем, как получить две нити, чтобы поделиться одной переменной.
Вам нужно просто заявить " а " в качестве глобального в thread2, так что вы'т изменение, которая является локальной для этой функции.
def thread2(threadname):
global a
while True:
a += 1
time.sleep(1)
В thread1, вы Don'т нужно ничего особенного делать, как долго, как вы Дон'т попробуйте изменить значение
а(что бы создать локальную переменную, что тени мирового; использовать
глобального` если нужно)>
def thread1(threadname):
#global a # Optional if you treat a as read-only
while a < 10:
print a
В функции:
a += 1
будет интерпретироваться компилятором как присвоить => создавать локальную переменную, которая является не то, что вы хотите. Он, вероятно, не с ошибкой не инициализирована
с (местными) на самом деле не был инициализирован:
>>> a = 1
>>> def f():
... a += 1
...
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in f
UnboundLocalError: local variable 'a' referenced before assignment
Вы можете получить то, что вы хотите с (очень неодобрительно, и на то есть веские причины) "глобального" сайта, вот так:
>>> def f():
... global a
... a += 1
...
>>> a
1
>>> f()
>>> a
2
Однако в целом, вы должны avoid используя глобальные переменные, которые становятся чрезвычайно быстро из рук. И это особенно актуально для многопоточных программ, где вы Don'т иметь какой-либо механизм синхронизации для вашего thread1 знать, когда " а " был изменен. Короче: потоки сложная, и вы не можете ожидать, чтобы иметь интуитивное понимание того, в какие события происходят, когда два (или более) потоков на одно и то же значение. Язык, компилятор, ОС, процессор... все это может сыграть свою роль, и решили изменить порядок операций для скорости, практичности или каких-либо других причин.
Правильный способ для такого рода вещей является использование инструментов обмена на Python (замки и друзей), или лучше, обмениваться данными через очереди вместо того, чтобы поделиться это, например, такой:
from threading import Thread
from queue import Queue
import time
def thread1(threadname, q):
#read variable "a" modify by thread 2
while True:
a = q.get()
if a is None: return # Poison pill
print a
def thread2(threadname, q):
a = 0
for _ in xrange(10):
a += 1
q.put(a)
time.sleep(1)
q.put(None) # Poison pill
queue = Queue()
thread1 = Thread( target=thread1, args=("Thread-1", queue) )
thread2 = Thread( target=thread2, args=("Thread-2", queue) )
thread1.start()
thread2.start()
thread1.join()
thread2.join()
Блокировки должны быть рассмотрены возможности использования, такие как резьбонарезание.Замок
. См. замок-объекты для получения дополнительной информации.
Принятый ответ может напечатать 10 по thread1, которые не то, что вы хотите. Вы можете запустить следующий код, чтобы легче понять ошибку.
def thread1(threadname):
while True:
if a % 2 and not a % 2:
print "unreachable."
def thread2(threadname):
global a
while True:
a += 1
С помощью замка можно запретить изменение "а", в то время как значение более чем один раз:
def thread1(threadname):
while True:
lock_a.acquire()
if a % 2 and not a % 2:
print "unreachable."
lock_a.release()
def thread2(threadname):
global a
while True:
lock_a.acquire()
a += 1
lock_a.release()
Если нить с помощью переменной в течение длительного времени, справляясь его в локальную переменную-первых, это хороший выбор.
Спасибо Джейсон Пан предложившего этот метод. В thread1, если заявление не атомная, так что пока что оператор выполняется, это's возможные для thread2 вторгаться в thread1, что позволяет добраться до кода добраться. Я'ве организованной идей из предыдущих постов в полную демонстрационную программу (ниже), что я бежал с Python 2.7.
С некоторыми вдумчивый анализ, Я'уверен, что мы могли бы получить дальнейшее понимание, но сейчас я думаю, что это's важный, чтобы продемонстрировать, что происходит, когда неатомарный поведение соответствует нитку.
# ThreadTest01.py - Demonstrates that if non-atomic actions on
# global variables are protected, task can intrude on each other.
from threading import Thread
import time
# global variable
a = 0; NN = 100
def thread1(threadname):
while True:
if a % 2 and not a % 2:
print("unreachable.")
# end of thread1
def thread2(threadname):
global a
for _ in range(NN):
a += 1
time.sleep(0.1)
# end of thread2
thread1 = Thread(target=thread1, args=("Thread1",))
thread2 = Thread(target=thread2, args=("Thread2",))
thread1.start()
thread2.start()
thread2.join()
# end of ThreadTest01.py
Как и было предсказано, в частности, в "недоступен" и код иногда фактически достигнутым, выпускающих продукцию.
Просто, чтобы добавить, когда я вставил замок приобретать пара/выхода в thread1 я обнаружил, что вероятность того, что в "недоступен" в печати сообщение было значительно уменьшено. Посмотреть сообщение я сократил время сна до 0,01 сек и увеличил НН до 1000.
С замком приобретать пара/выхода в thread1 я не'т ждать, чтобы увидеть сообщение, но это'ов нет. После того, как я вставил замок приобретать пара/выхода и в thread2, сообщение больше не появилось. В задних signt, оператор инкремента в thread2, наверное, тоже не атомной.
Ну, пример работает:
Предупреждение! НИКОГДА НЕ ДЕЛАЙТЕ ЭТОГО ДОМА/РАБОТЫ! Только в классе ;)
Использовать семафоры, общих переменных и т. д. чтобы избежать условий спешки.
from threading import Thread
import time
a = 0 # global variable
def thread1(threadname):
global a
for k in range(100):
print("{} {}".format(threadname, a))
time.sleep(0.1)
if k == 5:
a += 100
def thread2(threadname):
global a
for k in range(10):
a += 1
time.sleep(0.2)
thread1 = Thread(target=thread1, args=("Thread-1",))
thread2 = Thread(target=thread2, args=("Thread-2",))
thread1.start()
thread2.start()
thread1.join()
thread2.join()
а на выходе:
Thread-1 0
Thread-1 1
Thread-1 2
Thread-1 2
Thread-1 3
Thread-1 3
Thread-1 104
Thread-1 104
Thread-1 105
Thread-1 105
Thread-1 106
Thread-1 106
Thread-1 107
Thread-1 107
Thread-1 108
Thread-1 108
Thread-1 109
Thread-1 109
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110
Если сроки были правы, а += 100
операция будет пропущена:
Процессор выполняет при t А+100` и получает 104. Но он останавливается, и переходит к следующей теме Здесь, на Т+1 выполняет
а+1с старое значение, ``а == 4``. Так он вычисляет 5. Прыжок назад (при T+2), резьба 1, и писать
А=104в памяти. Теперь обратно на нить 2, Время Т+3 и запись
а=5` в памяти.
Вуаля! Следующая печать инструкция будет печатать 5 вместо 104.
Очень неприятный баг, чтобы быть воспроизведены и поймали.