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

조회수 8462회

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

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

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

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():
        ...
    

답변을 하려면 로그인이 필요합니다.

프로그래머스 커뮤니티는 개발자들을 위한 Q&A 서비스입니다. 로그인해야 답변을 작성하실 수 있습니다.

(ಠ_ಠ)
(ಠ‿ಠ)