Etc/2021

[Python3] Python3에서 교착 상태 감지하기 (hanging-threads 사용)

메바동 2021. 6. 25. 17:58
728x90

Python을 잘 사용하진 않지만 궁금해서 찾아보다 알게 된 내용을 정리하기로 하였다.

 

hanging-threads이라는 모듈을 사용하면 지정된 시간마다 스택을 덤프 하고 비교하여 변화가 없으면 교착 상태라고 판단한 다음 해당 스레드의 로그를 띄워주게 된다.

 

pip을 이용하여 우선 hanging-threads를 받아주어야 한다.

 

리눅스에서는 다음의 명령어로

pip install hanging-threads

 

윈도우에서는 다음의 명령어로 설치하면 된다.

py -m pip install hanging_threads

 

 

그다음은 교착 상태를 감지하고 싶은 소스에서

from hanging_threads import start_monitoring
monitoring_thread = start_monitoring(seconds_frozen=10, test_interval=100)

위의 코드를 작성하면 자동으로 감지가 된다.

seconds_frozen은 몇 초 동안 스택 덤프에 변화가 없으면 교착 상태로 판단하는지 지정하는 파라미터고,

test_interval은 스택 덤프를 작성하는 주기인 것 같다. (제대로 본 것이 아니라 맞는지 모르겠다.)

 

seconds_frozen은 초 단위이고, test_interval은 ms 단위이다.

 

 

테스트를 하기 위해 hang을 발생시키는 코드에 사용해 보았다.

 

from hanging_threads import start_monitoring
from urllib.request import urlopen

start_monitoring(seconds_frozen=10, test_interval=100)

url = 'http://localhost:5555'
response = urlopen(url)

print(response.read())

nc -l 5555

 

nc(netcat) 명령어로 5555 포트를 열어주고 위의 코드를 실행시키면 아무것도 읽히지 않기 때문에 hang이 발생하게 된다.

 

위 코드의 실행 결과는

 

 

위와 같이 나온다.

MainThread에서 hang이 발생했다는 알림을 띄어주고 해당 로그을 보여준다.

 

 

다음은 deadlock을 발생시키는 코드에 사용해 보았다.

 

from hanging_threads import start_monitoring
import threading
import time

start_monitoring(seconds_frozen=10, test_interval=100)

l1 = threading.Lock()
l2 = threading.Lock()

def f1(name):
    print('thread', name, 'about to lock l1')
    with l1:
        print('thread', name, 'has lock l1')
        time.sleep(0.3)
        print('thread', name, 'about to lock l2')
        with l2:
            print('thread', name, 'run into deadLock,\nthis line will never run')

def f2(name):
    print('thread', name, 'about to lock l2')
    with l2:
        print('thread', name, 'has lock l2')
        time.sleep(0.3)
        print('thread', name, 'about to lock l1')
        with l1:
            print('thread', name, 'run into deadlock,\nthis line will never run')

if __name__ == '__main__':
    t1 = threading.Thread(target=f1, args=['t1'])
    t2 = threading.Thread(target=f2, args=['t2'])

    t1.start()
    t2.start()

 

위 코드의 실행 결과는

 

 

위와 같이 나온다.

MainThread 말고 교착 상태가 발생한 두 스레드의 로그까지 출력한다.

 

 

찾고 나서 테스트해보니까 디버그 용도로 사용하기 좋을 것 같은데 그럴 거면 gdb를 사용하는 거랑 뭐가 다른지 모르겠다.

교착 상태가 발생했을 때 콜백 함수로 어떠한 조치를 취할 수 있도록 하는 기능이 있었으면 좋았을 것 같다.

 

 

Python 3.3부터 faulthandler를 사용하면 된다는 내용이 git에 적혀 있는데 다음에 시간이 생기면 이건 어떻게 사용하는 건지 알아봐야겠다. (근데 난 파이썬을 안 쓴다.)

728x90