특히 파이썬에서 변수는 스레드간에 어떻게 공유됩니까?
threading.Thread
전에 사용했지만 변수가 공유되는 방법에 대한 예를 실제로 이해하거나 본 적이 없습니다. 메인 스레드와 자식간에 공유됩니까, 아니면 자식간에 만 공유됩니까? 이 공유를 피하기 위해 언제 스레드 로컬 스토리지를 사용해야합니까?
잠금을 사용하여 스레드간에 공유 데이터에 대한 액세스를 동기화하는 것에 대한 많은 경고를 보았지만 아직 문제의 좋은 예를 보지 못했습니다.
미리 감사드립니다!
답변
파이썬에서는 함수 지역 변수를 제외한 모든 것이 공유됩니다 (각 함수 호출이 자체 지역 집합을 가져오고 스레드는 항상 별도의 함수 호출이기 때문입니다.) 그리고 심지어 변수 자체 만 (객체를 참조하는 이름) 함수에 국한됩니다. 객체 자체는 항상 전역 적이며 어떤 것이 든이를 참조 할 수 있습니다. Thread
특정 스레드에 대한 객체는이 점에서 특별한 개체가 아닙니다. Thread
전역 변수와 같이 모든 스레드가 액세스 할 수있는 위치 에 객체 를 저장하면 모든 스레드가 해당 Thread
객체에 액세스 할 수 있습니다 . 다른 스레드가 액세스 할 수있는 항목 을 원자 적으로 수정 하려면 잠금으로 보호해야합니다. 그리고 모든 스레드는 물론이 동일한 잠금을 공유해야합니다. 그렇지 않으면 그다지 효과적이지 않습니다.
실제 쓰레드-로컬 스토리지를 원한다면, 그것이 threading.local
온다. 속성은 threading.local
쓰레드간에 공유되지 않는다. 각 스레드는 자신이 배치 한 속성 만 봅니다. 구현에 대해 궁금한 경우 소스는 표준 라이브러리의 _threading_local.py 에 있습니다.
답변
다음 코드를 고려하십시오.
#/usr/bin/env python
from time import sleep
from random import random
from threading import Thread, local
data = local()
def bar():
print("I'm called from", data.v)
def foo():
bar()
class T(Thread):
def run(self):
sleep(random())
data.v = self.getName() # Thread-1 and Thread-2 accordingly
sleep(1)
foo()
>> T (). start (); T (). start () Thread-2에서 전화를 받았습니다. 나는 Thread-1에서 호출되었습니다.
여기서 threading.local ()은 foo ()의 인터페이스를 변경하지 않고 run ()에서 bar ()로 일부 데이터를 전달하는 빠르고 더러운 방법으로 사용됩니다.
전역 변수를 사용하는 것은 트릭을 수행하지 않습니다.
#/usr/bin/env python
from time import sleep
from random import random
from threading import Thread
def bar():
global v
print("I'm called from", v)
def foo():
bar()
class T(Thread):
def run(self):
global v
sleep(random())
v = self.getName() # Thread-1 and Thread-2 accordingly
sleep(1)
foo()
>> T (). start (); T (). start () Thread-2에서 전화를 받았습니다. Thread-2에서 전화를 받았습니다.
한편,이 데이터를 foo ()의 인수로 전달할 여유가 있다면 더 우아하고 잘 설계된 방법이 될 것입니다.
from threading import Thread
def bar(v):
print("I'm called from", v)
def foo(v):
bar(v)
class T(Thread):
def run(self):
foo(self.getName())
그러나 타사 또는 잘못 설계된 코드를 사용할 때 항상 가능한 것은 아닙니다.
답변
을 사용하여 스레드 로컬 저장소를 만들 수 있습니다 threading.local()
.
>>> tls = threading.local()
>>> tls.x = 4
>>> tls.x
4
TLS에 저장된 데이터는 각 스레드에 고유하므로 의도하지 않은 공유가 발생하지 않도록합니다.
답변
다른 모든 언어와 마찬가지로 Python의 모든 스레드는 동일한 변수에 액세스 할 수 있습니다. ‘메인 스레드’와 자식 스레드 사이에는 구분이 없습니다.
Python과의 한 가지 차이점은 Global Interpreter Lock은 한 번에 하나의 스레드 만 Python 코드를 실행할 수 있음을 의미합니다. 그러나 모든 일반적인 선점 문제가 여전히 적용되고 다른 언어에서와 마찬가지로 스레딩 기본 요소를 사용해야하므로 액세스 동기화에 대해서는 별 도움이되지 않습니다. 그러나 성능을 위해 스레드를 사용하고 있는지 다시 고려해야 함을 의미합니다.
답변
내가 틀렸을 수 있습니다. 다른 방법을 알고 있다면 이것이 왜 스레드 local ()을 사용해야하는지 설명하는 데 도움이 될 것입니다.
“다른 스레드가 액세스 할 수있는 항목을 원자 적으로 수정하려면 잠금으로 보호해야합니다.” 나는이 진술이-> 효과적으로 <-맞지만 완전히 정확하지는 않다고 생각합니다. “원자”라는 용어는 파이썬 인터프리터가 CPU에 인터럽트 신호를위한 공간을 남기지 않는 바이트 코드 청크를 생성했음을 의미한다고 생각했습니다.
원자 연산은 인터럽트에 대한 액세스 권한을 부여하지 않는 Python 바이트 코드 덩어리라고 생각했습니다. “running = True”와 같은 파이썬 문장은 원자 적입니다. 이 경우 인터럽트로부터 CPU를 잠글 필요가 없습니다 (나는 믿습니다). Python 바이트 코드 분석은 스레드 중단으로부터 안전합니다.
“threads_running [5] = True”와 같은 Python 코드는 원 자성이 아닙니다. 여기에는 두 개의 파이썬 바이트 코드가 있습니다. 하나는 객체에 대한 list ()를 역 참조하고 다른 바이트 코드 청크를 사용하여 객체에 값을 할당합니다 (이 경우 목록의 “장소”). 인터럽트는-> 두 바이트 코드-> 청크 <-사이에서 발생할 수 있습니다. 그것은 나쁜 일이 일어났습니다.
thread local ()은 “atomic”과 어떤 관련이 있습니까? 이것이 그 진술이 나에게 잘못된 것으로 보이는 이유입니다. 설명 할 수 없다면?