2009-09-25 128 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