Deadlock
스레드 간에 여러 리소스를 공유할 때 두 스레드가 각각 리소스의 일부를 점유하면서 동시에 서로의 리소스를 기다리면 교착 상태가 발생합니다. 교착 상태는 거의 발생하지 않지만 발생하면 응용 프로그램의 응답이 중지될 수 있습니다. 교착 상태의 예를 살펴보겠습니다.
# encoding: UTF-8 import threading import time class MyThread(threading.Thread): def do1(self): global resA, resB if mutexA.acquire(): msg = self.name+' got resA' print msg if mutexB.acquire(1): msg = self.name+' got resB' print msg mutexB.release() mutexA.release() def do2(self): global resA, resB if mutexB.acquire(): msg = self.name+' got resB' print msg if mutexA.acquire(1): msg = self.name+' got resA' print msg mutexA.release() mutexB.release() def run(self): self.do1() self.do2() resA = 0 resB = 0 mutexA = threading.Lock() mutexB = threading.Lock() def test(): for i in range(5): t = MyThread() t.start() if __name__ == '__main__': test()
실행 결과:
Thread-1 got resA
Thread-1 got resB
Thread-1이 resB를 얻었습니다
Thread-1이 resA를 얻었습니다
Thread-2가 resA를 얻었습니다
Thread-2가 resB를 얻었습니다
Thread-2가 resB를 얻었습니다
Thread-2가 resA를 얻었습니다
Thread-3이 resA를 얻었습니다
Thread-3이 resB를 얻었습니다
Thread-3이 resB를 얻었습니다
Thread-3에 resA가 있음
Thread-5에 resA가 있음
Thread-5에 resB가 있음
Thread-5에 resB가 있음
Thread-4가 resA를 얻었습니다
현재 프로세스가 종료되었습니다.
재진입 잠금
더 단순한 교착 상태 상황은 스레드가 동일한 리소스를 요청하기 위해 "반복"하여 교착 상태가 직접적으로 발생하는 경우입니다.
import threading import time class MyThread(threading.Thread): def run(self): global num time.sleep(1) if mutex.acquire(1): num = num+1 msg = self.name+' set num to '+str(num) print msg mutex.acquire() mutex.release() mutex.release() num = 0 mutex = threading.Lock() def test(): for i in range(5): t = MyThread() t.start() if __name__ == '__main__': test()
동일한 스레드에서 동일한 리소스에 대한 여러 요청을 지원하기 위해 Python은 "재진입 잠금"인 threading.RLock을 제공합니다. RLock은 내부적으로 Lock과 카운터 변수를 유지하며, 카운터는 획득 횟수를 기록하므로 리소스가 여러 번 필요할 수 있습니다. 스레드의 모든 획득이 해제될 때까지 다른 스레드가 리소스를 얻을 수 있습니다. 위의 예에서 Lock 대신 RLock을 사용하면 교착 상태가 발생하지 않습니다.
import threading import time class MyThread(threading.Thread): def run(self): global num time.sleep(1) if mutex.acquire(1): num = num+1 msg = self.name+' set num to '+str(num) print msg mutex.acquire() mutex.release() mutex.release() num = 0 mutex = threading.RLock() def test(): for i in range(5): t = MyThread() t.start() if __name__ == '__main__': test()
실행 결과:
Thread-1 set num 1로
Thread-3은 num을 2로 설정
Thread-2는 num을 3으로 설정
Thread-5는 num을 4로 설정
Thread-4 숫자를 5로 설정