상황 : 

특정 통신을 하는 부분에서 평소에는 잘 일어나지 않지만, 특정 케이스로 통신하는데 10초 이상 걸려 걸려 있던 lock이 풀리는 케이스가 생겨버렸다. 해당 경우 통신하는 함수에 timeout을 걸어, 특정 시간 이상 회신이 오지 않게 되면 다시 retry를 하여서 넘어가도록 하였다.

그를 위해서 python function timeout을 찾아보게 되었다.

처음 고민은 파이썬은 한 줄씩 실행되는데, 이게 오래 끌고 있는지 어떻게 확인하지였다.

보통은 함수 시작 전에 print 문으로 시간을 적어주고 함수가 실행 이후에 print문을 찍어주어 시간을 측정할 수 있지만, 이번 케이스는 함수가 실행 도중에 시간을 넘게 되면 retry해주는 것을 체크해줘야 하므로, 당황 스러웠다.

생각나는 것은 파이썬의 thread를 같이 돌리거나 해야 할 것 같은데 매우 비효율적으로 보였다.

그러던 중에 signal이라는 것을 알게 되었다.

사용법은 간단하였다.

직접 만들어서 쓸수도 있지만 나는 앞으로도 쓸 것 같아 데코레이터를 만드는 방법을 택했다.

직접 만들고 보니 신기하다.

 

from functools import wraps
import errno
import os
import signal

class TimeoutError(Exception):
    pass

def timeout(seconds=10, error_message=os.strerror(errno.ETIME)):
    def decorator(func):
        def _handle_timeout(signum, frame):
            raise TimeoutError(error_message)

        def wrapper(*args, **kwargs):
            signal.signal(signal.SIGALRM, _handle_timeout)
            signal.setitimer(signal.ITIMER_REAL,seconds) #used timer instead of alarm
            try:
                result = func(*args, **kwargs)
            finally:
                signal.alarm(0)
            return result
        return wraps(func)(wrapper)
    return decorator


# 여기서 부턴 개인적인 testing함수
@timeout(2)
def testing():
    import time
    count = 0
    while count < 3:
        print('helloworld', count)
        count += 1
        time.sleep(1)
    return count

위에 나온 부분이 어디서든 검색해보면 볼 수 있는 timeout decorator를 만드는 방법이다.

나는 testing이라는 특정함수를 2초 안에 실행되지 못하면 5회 다시 시도할 것이고,

그래도 안되면 이후 모션들을 취할 예정이다.

count = 0

while count < 5:
    try:
        testing()
        break
    except Exception as e:
        print('timeout occur', str(e))
        count += 1

실행 결과
helloworld 0
helloworld 1
time out occur Timer expired
helloworld 0
helloworld 1
time out occur Timer expired
helloworld 0
helloworld 1
time out occur Timer expired
helloworld 0
helloworld 1
time out occur Timer expired
helloworld 0
helloworld 1
time out occur Timer expired

# testing 함수가 끝나기 위해서는 3초가 필요한데,
# timeout은 2초를 걸어줘서 이런 증상이 발생한다.

testing 함수가 끝나기 위해서는 3초가 필요한데, timeout은 2초를 걸어줘서 이런 증상이 발생한다

이를 위해 만약 timeout 시간을 5초 이렇게 설정해주면

@timeout(5)
def testing():
    import time
    count = 0
    while count < 3:
        print('helloworld', count)
        count += 1
        time.sleep(1)
    return count

# 하고 다시 실행시켜주면

helloworld 0
helloworld 1
helloworld 2

나오고 끝나게 된다. => 5초 exception이 일어나기 전에 해당 함수가 끝나고 곧바로 break함수를 타고 빠져나오게 됨

 

참고 사이트 :

[[PYTHON] 파이썬에서 함수를 타임 아웃하는 방법, 타임 아웃이 1 초 미만입니다. 복붙노트](https://cnpnote.tistory.com/entry/PYTHON-%ED%8C%8C%EC%9D%B4%EC%8D%AC%EC%97%90%EC%84%9C-%ED%95%A8%EC%88%98%EB%A5%BC-%ED%83%80%EC%9E%84-%EC%95%84%EC%9B%83%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95-%ED%83%80%EC%9E%84-%EC%95%84%EC%9B%83%EC%9D%B4-1-%EC%B4%88-%EB%AF%B8%EB%A7%8C%EC%9E%85%EB%8B%88%EB%8B%A4)

[[Python] 함수(스크립트) 자동 타임아웃 설정 : 네이버 블로그](https://m.blog.naver.com/PostView.nhn?blogId=stop2y&logNo=221370575532&proxyReferer=https%3A%2F%2Fwww.google.co.kr%2F)

[python - 함수가 완료되는데에 너무 오래 걸릴 때, timeout시키기 | Hashcode](https://hashcode.co.kr/questions/2203/%ED%95%A8%EC%88%98%EA%B0%80-%EC%99%84%EB%A3%8C%EB%90%98%EB%8A%94%EB%8D%B0%EC%97%90-%EB%84%88%EB%AC%B4-%EC%98%A4%EB%9E%98-%EA%B1%B8%EB%A6%B4-%EB%95%8C-timeout%EC%8B%9C%ED%82%A4%EA%B8%B0)

 

+ Recent posts