Python의 "스레드 로컬 저장소"란 무엇이며 왜 필요합니까?


100

특히 파이썬에서 변수는 스레드간에 어떻게 공유됩니까?

threading.Thread전에 사용했지만 변수가 공유되는 방법에 대한 예를 실제로 이해하거나 본 적이 없습니다. 메인 스레드와 자식간에 공유됩니까, 아니면 자식간에 만 공유됩니까? 이 공유를 피하기 위해 언제 스레드 로컬 스토리지를 사용해야합니까?

잠금을 사용하여 스레드간에 공유 데이터에 대한 액세스를 동기화하는 것에 대한 많은 경고를 보았지만 아직 문제의 좋은 예를 보지 못했습니다.

미리 감사드립니다!


2
제목이 질문과 일치하지 않습니다. 문제는 스레드간에 변수를 공유하는 것과 관련이 있습니다. 제목은 스레드 로컬 스토리지에 관한 것임을 암시합니다.
Casebash

2
@Casebash :이 질문의 소리에서 Mike는 공유 데이터로 인한 문제를 피하기 위해 TLS가 필요하다는 것을 읽었지만 기본적으로 공유되는 데이터, 공유 대상 및 공유 방법에 대해서는 명확하지 않았습니다. 질문에 더 잘 맞도록 제목을 조정했습니다.
Shog9

답변:


83

파이썬에서는 함수 지역 변수를 제외한 모든 것이 공유됩니다 (각 함수 호출이 자체 지역 집합을 가져오고 스레드는 항상 별도의 함수 호출이기 때문입니다.) 그리고 심지어 변수 자체 만 (객체를 참조하는 이름) 함수에 국한됩니다. 객체 자체는 항상 전역 적이며 어떤 것이 든이를 참조 할 수 있습니다. Thread특정 스레드에 대한 객체는이 점에서 특별한 개체가 아닙니다. Thread전역 변수와 같이 모든 스레드가 액세스 할 수있는 위치 에 객체 를 저장하면 모든 스레드가 해당 Thread객체에 액세스 할 수 있습니다 . 다른 스레드가 액세스 할 수있는 항목 을 원자 적으로 수정 하려면 잠금으로 보호해야합니다. 그리고 모든 스레드는 물론이 동일한 잠금을 공유해야합니다. 그렇지 않으면 그다지 효과적이지 않습니다.

실제 쓰레드-로컬 스토리지를 원한다면, 그것이 threading.local온다. 속성은 threading.local쓰레드간에 공유되지 않는다. 각 스레드는 자신이 배치 한 속성 만 봅니다. 구현에 대해 궁금한 경우 소스는 표준 라이브러리의 _threading_local.py 에 있습니다.


1
다음 문장에 대해 더 자세히 설명해 주시겠습니까? "이 같은 스레드에서 생성 한 것이 아니라 다른 스레드가 접근 할 수있는 곳에 저장하지 않은 것을 원자 적으로 수정하려면 잠금으로 보호해야합니다."
changyuheng

@changyuheng : 다음은 원자 적 동작에 대한 설명입니다. cs.nott.ac.uk/~psznza/G52CON/lecture4.pdf
Tom Busby

1
@TomBusby : 접근 할 수있는 다른 스레드가없는 경우 잠금으로 보호해야하는 이유, 즉 프로세스를 원자 적으로 만들어야하는 이유는 무엇입니까?
changyuheng

2
"객체 자체는 항상 전역 적이며, 무엇이든 참조 할 수 있습니다."라는 간단한 예를 들어 주시겠습니까? 참조로 당신은 읽기를 의미하고 할당 / 추가하지 않는다고 가정합니까?
변수


75

다음 코드를 고려하십시오.

#/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())

그러나 타사 또는 잘못 설계된 코드를 사용할 때 항상 가능한 것은 아닙니다.


18

을 사용하여 스레드 로컬 저장소를 만들 수 있습니다 threading.local().

>>> tls = threading.local()
>>> tls.x = 4 
>>> tls.x
4

TLS에 저장된 데이터는 각 스레드에 고유하므로 의도하지 않은 공유가 발생하지 않도록합니다.


2

다른 모든 언어와 마찬가지로 Python의 모든 스레드는 동일한 변수에 액세스 할 수 있습니다. '메인 스레드'와 자식 스레드 사이에는 구분이 없습니다.

Python과의 한 가지 차이점은 Global Interpreter Lock은 한 번에 하나의 스레드 만 Python 코드를 실행할 수 있음을 의미합니다. 그러나 모든 일반적인 선점 문제가 여전히 적용되고 다른 언어에서와 마찬가지로 스레딩 기본 요소를 사용해야하므로 액세스 동기화에 대해서는 별 도움이되지 않습니다. 그러나 성능을 위해 스레드를 사용하고 있는지 다시 고려해야 함을 의미합니다.


0

내가 틀렸을 수 있습니다. 다른 방법을 알고 있다면 이것이 왜 스레드 local ()을 사용해야하는지 설명하는 데 도움이 될 것입니다.

"다른 스레드가 액세스 할 수있는 항목을 원자 적으로 수정하려면 잠금으로 보호해야합니다." 나는이 진술이-> 효과적으로 <-맞지만 완전히 정확하지는 않다고 생각합니다. "원자"라는 용어는 파이썬 인터프리터가 CPU에 인터럽트 신호를위한 공간을 남기지 않는 바이트 코드 청크를 생성했음을 의미한다고 생각했습니다.

원자 연산은 인터럽트에 대한 액세스 권한을 부여하지 않는 Python 바이트 코드 덩어리라고 생각했습니다. "running = True"와 같은 파이썬 문장은 원자 적입니다. 이 경우 인터럽트로부터 CPU를 잠글 필요가 없습니다 (나는 믿습니다). Python 바이트 코드 분석은 스레드 중단으로부터 안전합니다.

"threads_running [5] = True"와 같은 Python 코드는 원 자성이 아닙니다. 여기에는 두 개의 파이썬 바이트 코드가 있습니다. 하나는 객체에 대한 list ()를 역 참조하고 다른 바이트 코드 청크를 사용하여 객체에 값을 할당합니다 (이 경우 목록의 "장소"). 인터럽트는-> 두 바이트 코드-> 청크 <-사이에서 발생할 수 있습니다. 그것은 나쁜 일이 일어났습니다.

thread local ()은 "atomic"과 어떤 관련이 있습니까? 이것이 그 진술이 나에게 잘못된 것으로 보이는 이유입니다. 설명 할 수 없다면?


1
이것은 대답처럼 보이지만 문제가있는 것으로보고 된 것 같습니다. 나는 대답에 대한 설명을 요구하지 않을 것입니다. 이것이 댓글의 목적입니다.
Dharman
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.