2016-06-29 2 views
2

가정하자 :`with` 문은 __enter__이고 __exit__는 thread 안전합니까?

class A(object): 
    def __init__(self): 
    self.cnt = 0 
    def __enter__(self): 
    self.cnt += 1 
    def __exit__(self, exc_type, exc_value, traceback) 
    self.cnt -= 1 
  1. 그것은 self.cnt += 1 두 번 때 멀티 스레딩을 실행할 수 있음을 수 있습니까?
  2. 멀티 스레딩에서 동일한 컨텍스트 관리자 인스턴스의 경우 __enter__을 두 번 호출하고 __exit__을 한 번만 호출 할 수 있으므로 self.cnt 최종 결과는 1입니까?
+0

멀티 스레딩에서 같은 인스턴스가 무슨 뜻입니까? 관계없이 각각의 스레드에게 당신이 사용하고있는 컨텍스트 관리자의 인스턴스를 제공한다면 관련성이 없습니다. 액세스 된 데이터가 스레드간에 공유되는 경우, 액세스는 mutex/lock을 사용하여 관리해야합니다. – metatoaster

+0

@metatoaster 각 스레드마다 고유 한 컨텍스트 관리자가 있지만 동의합니다. 동료는 __enter__이 인스턴스 메소드 일 뿐이며 두 번 호출 될 수 있으므로 스레드 안전하지 않으므로 동일한 contextmanager 인스턴스의 __enter__은 스레드가 아닙니다. 안전한. 나는 잃어버린 느낌을 느낀다 – est

+1

각 스레드가 자신의 인스턴스를 가지고 있다면 공유 된 데이터가 없다. – martineau

답변

2

아니요, 스레드 안전은 잠금 장치를 통해서만 보장 될 수 있습니다.

self.cnt += 1은 멀티 스레딩 할 때 두 번 실행될 수 있습니까?

두 개의 스레드가 실행 중이면 두 번 실행됩니다. 세 스레드, 세 번, 등등. 나는 당신이 당신이 정말로 당신이 콘텍스트 관리자와 관련하여 이러한 스레드를 만들고 실행하는 방법을 보여줄지 모르겠다.

은 같은 맥락 관리자 예를 들어, 멀티 스레딩에, 어떻게 든 __enter__ 두 번 호출하고 __exit__ 한 번만 호출 할 수 있기 때문에 self.cnt 최종 결과가 1이라고 할 수 있습니까?

예, 최종 결과는 0이 아니지만 입력 및 종료의 비대칭 호출로 가정하는 메커니즘을 통한 것은 아닙니다. 심지어 최종 shared_context.cnt 자주 0 다시 결국하지 않는 것이

from threading import Thread 

class Context(object): 
    def __init__(self): 
     self.cnt = 0 
    def __enter__(self): 
     self.cnt += 1 
    def __exit__(self, exc_type, exc_value, traceback): 
     self.cnt -= 1 

shared_context = Context() 

def run(thread_id): 
    with shared_context: 
     print('enter: shared_context.cnt = %d, thread_id = %d' % (
      shared_context.cnt, thread_id)) 
     print('exit: shared_context.cnt = %d, thread_id = %d' % (
      shared_context.cnt, thread_id)) 

threads = [Thread(target=run, args=(i,)) for i in range(1000)] 

# Start all threads 
for t in threads: 
    t.start() 

# Wait for all threads to finish before printing the final cnt 
for t in threads: 
    t.join() 

print(shared_context.cnt) 

당신은 불가피하게 발견 할 것이다 : 여러 스레드에서 동일한 컨텍스트 관리 프로그램 인스턴스를 사용하는 경우, 당신은 너무 같은 오류를 재현 할 수있는 간단한 예를 구성 할 수 있습니다 네 옵 코드로 해석되는이 대부분 += 운영자에 의해 발생

enter: shared_context.cnt = 3, thread_id = 998 
exit: shared_context.cnt = 3, thread_id = 998 
enter: shared_context.cnt = 3, thread_id = 999 
exit: shared_context.cnt = 3, thread_id = 999 
2 
... 
enter: shared_context.cnt = 0, thread_id = 998 
exit: shared_context.cnt = 0, thread_id = 998 
enter: shared_context.cnt = 1, thread_id = 999 
exit: shared_context.cnt = 0, thread_id = 999 
-1 

: 모든 스레드가 시작 시작 및 종료 관련 모든 더 많거나 적은 쌍으로 호출 된 경우에도, 동일한 코드로 끝나면하지만 개별 opcode만이 보장됩니다. GIL에 의해서만 안전하다. 자세한 내용은이 질문에서 찾을 수 있습니다 : Is the += operator thread-safe in Python?

+0

공유 컨텍스트를 사용하지 않으면'with shared_context :'를'Context() '로 변경하면 여전히'1 '보다 커질 수 있습니까? – est

+0

또 다른 질문은 예제 코드와 함께,'shared_context.cnt'의 최종 결과 ('joinall()')가 여전히 0으로 보장 될까요? – est

+0

@est : 당신은 그것을 바꿀 수 있고 직접 볼 수 있습니다.만약 당신이'Context()'를 사용했다면, 코드 사이에 쓰레드간에 공유 된 데이터가 없을 것입니다. joinall() 호출은 없다. (모든 스레드에 대한 것을 의미하지 않는 한 그것들에 합류하라.)하지만 포함 된 shared_context.cnt에 대한 결과는 그것이 0이 될 수 없다는 것을 보여준다. – metatoaster

관련 문제