2009-09-25 131 views
3

我需要动态加载代码(作为源代码),运行它并获取结果。我加载的代码总是包含一个run方法,它返回所需的结果。一切看起来都非常的简单,因为在Python往常一样,因为我可以做Python:时间方法调用,并在超过时间时停止它

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

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

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

编辑:按照dcrosta的要求清除事情:加载的代码不是不可信的,但在机器上自动生成。其目的是遗传编程。

+1

即使是来自可靠来源,你可以只使用'进口source',每当它的变化,用'重装(源)'。这样,您不会污染全局名称空间。你必须使用'source.run()'。 – voyager 2009-09-25 14:53:38

回答

3

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

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

如果您不需要严格序列化您的函数的执行,而是可以安排您的体系结构并行地尝试其中两个或更多,并且正在多核机器上运行(或者多核机器上运行快速局域网),那么突然多处理成为一个高性能的解决方案,很容易回报产卵和IPC的开销等等,正是因为你可以利用尽可能多的处理器(或集群中的节点)。

0

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

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

+0

它可能来源于可靠来源。问题是一样的,即使他进口它。在给定的安装时间结束或结束之前,如何执行一个方法? – voyager 2009-09-25 14:32:28

0

快速谷歌为“蟒蛇超时”透着TimeoutFunction

+0

这是一个非常优秀的发现,唯一的问题是signal.SIGALRM仅限于Unix。 – Ash 2009-09-25 14:46:40

2

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

2

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

1

我可以酿出这一个新的线程,并指定。加入)时间(方法,但我不能轻易得到的结果出来的

如果超时,这意味着该方法没有完成,所以没有结果。如果你有增量结果,你可以将它们存储在某个地方,然后将它们读出来(不管你喜欢什么)(记住threadsafety)。

使用基于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()调用。

0

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

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

+1

为了将来的参考,当你建议一个包来包含它与上下文的相关性时,你应该多加一点解释。 – 2015-03-07 12:55:49