[python] 스레드와 함께 전역 변수 사용

스레드와 전역 변수를 어떻게 공유합니까?

내 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

예상대로 예제를 실행할 때 실제로 “도달 할 수없는”코드에 도달하여 출력을 생성합니다.

추가로 스레드 1에 잠금 획득 / 해제 쌍을 삽입했을 때 “도달 할 수 없음”메시지가 인쇄 될 가능성이 크게 줄어든다는 것을 알았습니다. 메시지를보기 위해 수면 시간을 0.01 초로 줄이고 NN을 1000으로 늘 렸습니다.

thread1에서 잠금 획득 / 해제 쌍을 사용하면 메시지가 전혀 표시되지 않을 것으로 예상했지만 거기에 있습니다. 잠금 획득 / 해제 쌍도 thread2에 삽입 한 후 메시지가 더 이상 나타나지 않습니다. 뒷부분에서 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

타이밍이 a += 100맞으면 작업을 건너 뜁니다.

프로세서는 T에서 실행 a+100하고 (104)를 얻을 수 그러나 그것은 중지하고, 여기에 다음 스레드에에서 T + 1이 실행 점프 a+1하는 오래된 값을 a == 4. 따라서 5를 계산합니다. (T + 2에서) 스레드 1로 이동하고 a=104메모리에 씁니다 . 이제 스레드 2에서 시간은 T + 3이고 a=5메모리에 기록 합니다. 짜잔! 다음 인쇄 명령은 104 대신 5를 인쇄합니다.

재현 및 잡을 수있는 매우 불쾌한 버그.


답변