Python:调用方法的时间,如果超过时间则停止它

时间:2009-09-25 14:19:36

标签: python multithreading

我需要动态加载代码(作为源代码),运行它并获得结果。我加载的代码总是包含一个run方法,它返回所需的结果。像往常一样,一切看起来都非常简单,因为我可以做到

exec(source) #source includes run() definition
result = run(params)
#do stuff with result

唯一的问题是,动态生成的代码中的run()方法可能无法终止,所以我只需要运行它最多x秒。我可以为此生成一个新线程,并为.join()方法指定一个时间,但是我不能轻易地从中得到结果(或者我可以)。性能也是一个需要考虑的问题,因为所有这些都是在很长一段时间内发生的

有关如何进行的任何建议?

编辑:根据dcrosta的请求清理:加载的代码不是不受信任的,而是在机器上自动生成的。其目的是遗传编程。

7 个答案:

答案 0 :(得分:4)

唯一“非常好”的解决方案 - 基本上没有开销 - 将基于SIGALRM,直接或通过一个漂亮的抽象层;但正如已经说过Windows不支持这一点。线程是没有用的,不是因为它很难得到结果(这对于Queue来说是微不足道的!),但是因为以一种漂亮的跨平台方式强行终止一个失控的线程是不可行的。

这使得高开销multiprocessing成为唯一可行的跨平台解决方案。您需要一个进程池来减少进程产生的开销(因为可能只需要偶尔杀死一个失控函数,大多数情况下您可以通过发送新函数来重用现有进程)。同样,Queue(多处理类型)使得结果变得容易(尽管比线程情况更加谨慎,因为在多处理情况下可能出现死锁)。

如果您不需要严格序列化函数的执行,而是可以安排您的架构并行尝试其中的两个或多个,并且在多核机器上运行(或者快速运行多台机器) LAN),然后突然多处理成为一种高性能解决方案,轻松地为产生和IPC开销等进行回报,这正是因为您可以利用尽可能多的处理器(或集群中的节点)。

答案 1 :(得分:2)

您可以使用multiprocessing库在单独的进程中运行代码,并在进程上调用.join()以等待它完成,并将timeout参数设置为您想要的任何内容。该库提供了几种从另一个进程获取数据的方法 - 使用Value对象(在该页面上的共享内存示例中看到)可能就足够了。如果确实需要,可以在进程上使用terminate()调用,但不推荐使用。

答案 2 :(得分:2)

你也可以使用Stackless Python,因为它允许cooperative scheduling微线程。您可以在此处指定返回前要执行的最大指令数。设置例程并获取返回值虽然有点棘手。

答案 3 :(得分:1)

  

我可以为此生成一个新线程,并指定.join()方法的时间,但是我不能轻易地从中得到结果

如果超时到期,则意味着该方法没有完成,因此无法获得结果。如果你有增量结果,你可以将它们存储在某个地方并随意读出它们(记住线程安全)。

使用基于SIGALRM的系统非常有用,因为它可以随时提供异步信号,即使在你不期望的那个或者最后一个处理程序中也是如此。 (不幸的是,其他语言更好地解决了这个问题。)例如:

try:
    # code
finally:
    cleanup1()
    cleanup2()
    cleanup3()

通过SIGALRM传递的信号可能在cleanup2()期间发生,这将导致cleanup3()永远不会被执行。 Python根本没有办法以不合作和安全的方式终止正在运行的线程。

您应该让代码自行检查超时。

import threading
from datetime import datetime, timedelta

local = threading.local()
class ExecutionTimeout(Exception): pass

def start(max_duration = timedelta(seconds=1)):
    local.start_time = datetime.now()
    local.max_duration = max_duration

def check():
    if datetime.now() - local.start_time > local.max_duration:
        raise ExecutionTimeout()

def do_work():
    start()
    while True:
        check()
        # do stuff here
    return 10

try:
    print do_work()
except ExecutionTimeout:
    print "Timed out"

(当然,这属于一个模块,因此代码实际上看起来像“timeout.start()”;“timeout.check()”。)

如果你动态生成代码,那么在每个循环开始时生成一个timeout.check()调用。

答案 4 :(得分:1)

考虑使用stopit包,在某些情况下您需要超时控制。它的文件强调了局限性。

https://pypi.python.org/pypi/stopit

答案 5 :(得分:0)

执行不受信任的代码是危险的,通常应该避免,除非不可能这样做。我认为你担心run()方法的时间是正确的,但run()方法也可以做其他事情:删除所有文件,打开套接字并建立网络连接,开始破解你的密码和电子邮件结果回到攻击者等等。

也许如果您可以提供有关动态加载代码的更多细节,SO社区可以帮助建议替代方案。

答案 6 :(得分:0)

快速google for“python timeout”会显示TimeoutFunction