함수가 완료되는데에 너무 오래 걸릴 때, timeout시키기


스크린샷을 찍고 싶은 사이트에 방문할 수 있도록, 해당 사이트들의 URL을 포함한 텍스트 파일을 순차적으로 읽어내리는 쉘 스크립트를 하나 만들었습니다.

일단 문제없이 간단하게 구현하였습니다. 스크립트는 리스트에 올라와있는 각각의 사이트에 방문하여 스크린샷을 생성하는 클래스를 초기화합니다. 그런데 몇몇 사이트는 로딩이 완료되기까지 상당히 오랜 시간이 걸리고, 몇몇은 아예 로딩에 실패하기도 하더군요. 그래서 10초 내에 작업을 완료할 수 없으면 False를 리턴할 수 있도록, 스크린샷을 찍는 함수를 timeout 스크립트로 감싸려고 합니다.

10초가 지나면 False를 리턴하는 비동기적 타이머를 설정한다든가, 함수가 내부적으로 어떻게 작동하든 단순한 해결방법으로도 상관없습니다.

  • 2016년 06월 03일에 작성됨

조회수 146


1 답변


좋아요
0
싫어요
채택취소하기

명령을 timeout시키는 프로세스가 signal과 관련한 문서에 명시되어 있습니다.

기본적인 아이디어는 특정 시간 간격의 알람을 설정하는 시그널 핸들러를 사용하여 시간이 지나면 예외를 발생시키는 것입니다.

참고로 이 방식은 UNIX에서만 작동합니다.

아래 코드는 데코레이터를 생성하는 코드입니다. (해당 코드를 timeout.py로 저장하세요.)

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.alarm(seconds)
            try:
                result = func(*args, **kwargs)
            finally:
                signal.alarm(0)
            return result

        return wraps(func)(wrapper)

    return decorator

위 코드는 어떤 긴 작업의 함수에던지 적용할 수 있는 @timeout을 호출할 수 있는 데코레이터를 생성해줍니다.

따라서, 질문하신 분의 코드에 이 데코레이터를 아래와 같이 사용할 수 있습니다 :

from timeout import timeout

# 기본 만료시간인 10초 뒤 함수를 타임아웃
@timeout
def long_running_function1():
    ...

# 5초 뒤에 타임아웃
@timeout(5)
def long_running_function2():
    ...

# "Connection timed out"이라는 에러와 함께 30초 뒤 타임아웃
@timeout(30, os.strerror(errno.ETIMEDOUT))
def long_running_function3():
    ...
  • 2016년 06월 04일에 작성됨

로그인이 필요한 기능입니다.

Hashcode는 개발자들을 위한 무료 QnA사이트 입니다. 작성한 답변에 다른 개발자들이 댓글을 작성하거나 좋아요/싫어요를 할 수 있기 때문에 계정을 필요로 합니다.
► 로그인
► 계정만들기
Close