스레드와 함께 전역 변수 사용
스레드와 전역 변수를 어떻게 공유합니까?
내 Python 코드 예는 다음과 같습니다.
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()
두 스레드가 하나의 변수를 공유하도록하는 방법을 모르겠습니다.
a
에서 전역 으로 선언하기 만하면 해당 함수에 로컬 인을 thread2
수정하지 않습니다 a
.
def thread2(threadname):
global a
while True:
a += 1
time.sleep(1)
에서 thread1
당신의 가치 수정하려고하지 않는 한, 당신은 아무것도 특별 할 필요가 없습니다 a
(그림자 글로벌 한 것을 지역 변수를 만들 것이다 사용 global a
이 필요한 경우)를>
def thread1(threadname):
#global a # Optional if you treat a as read-only
while a < 10:
print a
함수에서 :
a += 1
컴파일러 assign to a => Create local variable a
는 원하는 대로 해석 되지 않습니다. a not initialized
(로컬) a가 실제로 초기화되지 않았으므로 오류 와 함께 실패 할 수 있습니다.
>>> 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
다음과 같이 (매우 눈살을 찌푸리고 좋은 이유로) global
키워드로 원하는 것을 얻을 수 있습니다 .
>>> def f():
... global a
... a += 1
...
>>> a
1
>>> f()
>>> a
2
그러나 일반적으로 매우 빠르게 손에서 벗어난 전역 변수를 사용 하지 않아야합니다 . 그리고 이것은 수정 된 thread1
시기를 알 수있는 동기화 메커니즘이없는 다중 스레드 프로그램의 경우 특히 그렇습니다 a
. 간단히 말해서 , 스레드는 복잡 하며 두 개 이상의 스레드가 동일한 값에서 작동 할 때 이벤트가 발생하는 순서를 직관적으로 이해할 수 없습니다. 언어, 컴파일러, OS, 프로세서 ... 모두가 역할을 수행 할 수 있으며 속도, 실용성 또는 기타 이유로 작업 순서를 수정할 수 있습니다.
이런 종류의 적절한 방법은 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()
와 같은 잠금을 사용하는 것으로 간주해야합니다 threading.Lock
. 자세한 내용은 lock-objects 를 참조하십시오.
받아 들여지는 대답은 원하는 것이 아닌 thread1로 10을 인쇄 할 수 있습니다. 다음 코드를 실행하여 버그를 더 쉽게 이해할 수 있습니다.
def thread1(threadname):
while True:
if a % 2 and not a % 2:
print "unreachable."
def thread2(threadname):
global a
while True:
a += 1
잠금을 사용하면 a
두 번 이상 읽는 동안 변경을 금지 할 수 있습니다 .
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()
스레드가 오랫동안 변수를 사용하는 경우 먼저 로컬 변수에 대처하는 것이 좋은 선택입니다.
이 방법을 제안 해 주신 Jason Pan에게 감사드립니다. thread1 if 문은 원자 적이 지 않으므로 해당 문이 실행되는 동안 thread2가 thread1에 침입하여 도달 할 수없는 코드에 도달 할 수 있습니다. 이전 게시물의 아이디어를 Python 2.7로 실행 한 완전한 데모 프로그램 (아래)으로 구성했습니다.
신중한 분석을 통해 더 많은 통찰력을 얻을 수 있다고 확신하지만, 지금은 비원 자적 동작이 스레딩을 만날 때 어떤 일이 발생하는지 보여주는 것이 중요하다고 생각합니다.
# 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
예상대로 예제를 실행할 때 실제로 "도달 할 수없는"코드에 도달하여 출력을 생성합니다.
Just to add, when I inserted a lock acquire/release pair into thread1 I found that the probability of having the "unreachable" message print was greatly reduced. To see the message I reduced the sleep time to 0.01 sec and increased NN to 1000.
With a lock acquire/release pair in thread1 I didn't expect to see the message at all, but it's there. After I inserted a lock acquire/release pair also into thread2, the message no longer appeared. In hind signt, the increment statement in thread2 probably also is non-atomic.
Well, running example:
WARNING! NEVER DO THIS AT HOME/WORK! Only in classroom ;)
Use semaphores, shared variables, etc. to avoid rush conditions.
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()
and the output:
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
If the timing were right, the a += 100
operation would be skipped:
Processor executes at T a+100
and gets 104. But it stops, and jumps to next thread Here, At T+1 executes a+1
with old value of a, a == 4
. So it computes 5. Jump back (at T+2), thread 1, and write a=104
in memory. Now back at thread 2, time is T+3 and write a=5
in memory. Voila! The next print instruction will print 5 instead of 104.
VERY nasty bug to be reproduced and caught.
참고URL : https://stackoverflow.com/questions/19790570/using-a-global-variable-with-a-thread
'programing tip' 카테고리의 다른 글
WebGL에서 속성, 균일 및 가변 변수의 차이점은 무엇입니까? (0) | 2020.11.11 |
---|---|
Git 숨김 두 번 (0) | 2020.11.11 |
@Id 및 @GeneratedValue (strategy = GenerationType.IDENTITY) 주석의 사용은 무엇입니까? (0) | 2020.11.11 |
pip 자체의 버전을 아는 방법 (0) | 2020.11.11 |
Bittorent 클라이언트의 DHT는 어떻게 "부트 스트랩"됩니까? (0) | 2020.11.11 |