超时功能,如果完成时间太长[重复]

时间:2021-07-10 21:21:33

This question already has an answer here:

这个问题已经有了答案:

I have a shell script that loops through a text file containing URL:s that I want to visit and take screenshots of.

我有一个shell脚本,它循环遍历一个包含URL的文本文件:我想访问并截屏。

All this is done and simple. The script initializes a class that when run creates a screenshot of each site in the list. Some sites take a very, very long time to load, and some might not be loaded at all. So I want to wrap the screengrabber-function in a timeout script, making the function return False if it couldn't finish within 10 seconds.

所有这些都已经完成,而且很简单。脚本初始化一个类,该类在运行时创建列表中每个站点的屏幕快照。有些网站需要很长时间才能加载,而有些网站可能根本就不会加载。因此,我想在一个超时脚本中封装screengrabber函数,如果在10秒内无法完成,函数将返回False。

I'm content with the simplest solution possible, maybe setting a asynchronous timer that will return False after 10 seconds no matter what actually happens inside the function?

我对最简单的解决方案很满意,也许可以设置一个异步计时器,10秒后返回False,不管函数内部实际发生了什么?

2 个解决方案

#1


200  

The process for timing out an operations is described in the documentation for signal.

在信号的文档中描述了操作超时的过程。

The basic idea is to use signal handlers to set an alarm for some time interval and raise an exception once that timer expires.

基本思想是使用信号处理程序为一段时间间隔设置警报,并在计时器过期时引发异常。

Note that this will only work on UNIX.

注意,这只适用于UNIX。

Here's an implementation that creates a decorator (save the following code as timeout.py).

这里有一个创建decorator的实现(将以下代码保存为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

This creates a decorator called @timeout that can be applied to any long running functions.

这将创建一个名为@timeout的decorator,可以应用于任何长期运行的函数。

So, in your application code, you can use the decorator like so:

因此,在应用程序代码中,可以使用decorator,如下所示:

from timeout import timeout

# Timeout a long running function with the default expiry of 10 seconds.
@timeout
def long_running_function1():
    ...

# Timeout after 5 seconds
@timeout(5)
def long_running_function2():
    ...

# Timeout after 30 seconds, with the error "Connection timed out"
@timeout(30, os.strerror(errno.ETIMEDOUT))
def long_running_function3():
    ...

#2


125  

I rewrote David's answer using the with statement, it allows you do do this:

我用" with "的语句改写了David的答案,它允许你这样做:

with timeout(seconds=3):
    time.sleep(4)

Which will raise a TimeoutError.

这会引起一场恐慌。

The code is still using signal and thus UNIX only:

代码仍然使用信号,因此只使用UNIX:

import signal

class timeout:
    def __init__(self, seconds=1, error_message='Timeout'):
        self.seconds = seconds
        self.error_message = error_message
    def handle_timeout(self, signum, frame):
        raise TimeoutError(self.error_message)
    def __enter__(self):
        signal.signal(signal.SIGALRM, self.handle_timeout)
        signal.alarm(self.seconds)
    def __exit__(self, type, value, traceback):
        signal.alarm(0)

#1


200  

The process for timing out an operations is described in the documentation for signal.

在信号的文档中描述了操作超时的过程。

The basic idea is to use signal handlers to set an alarm for some time interval and raise an exception once that timer expires.

基本思想是使用信号处理程序为一段时间间隔设置警报,并在计时器过期时引发异常。

Note that this will only work on UNIX.

注意,这只适用于UNIX。

Here's an implementation that creates a decorator (save the following code as timeout.py).

这里有一个创建decorator的实现(将以下代码保存为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

This creates a decorator called @timeout that can be applied to any long running functions.

这将创建一个名为@timeout的decorator,可以应用于任何长期运行的函数。

So, in your application code, you can use the decorator like so:

因此,在应用程序代码中,可以使用decorator,如下所示:

from timeout import timeout

# Timeout a long running function with the default expiry of 10 seconds.
@timeout
def long_running_function1():
    ...

# Timeout after 5 seconds
@timeout(5)
def long_running_function2():
    ...

# Timeout after 30 seconds, with the error "Connection timed out"
@timeout(30, os.strerror(errno.ETIMEDOUT))
def long_running_function3():
    ...

#2


125  

I rewrote David's answer using the with statement, it allows you do do this:

我用" with "的语句改写了David的答案,它允许你这样做:

with timeout(seconds=3):
    time.sleep(4)

Which will raise a TimeoutError.

这会引起一场恐慌。

The code is still using signal and thus UNIX only:

代码仍然使用信号,因此只使用UNIX:

import signal

class timeout:
    def __init__(self, seconds=1, error_message='Timeout'):
        self.seconds = seconds
        self.error_message = error_message
    def handle_timeout(self, signum, frame):
        raise TimeoutError(self.error_message)
    def __enter__(self):
        signal.signal(signal.SIGALRM, self.handle_timeout)
        signal.alarm(self.seconds)
    def __exit__(self, type, value, traceback):
        signal.alarm(0)