2011-05-24 91 views
19

我對如何在python/twisted中編寫異步代碼有點困惑。假設(爲了討論)我露出一個功能向世界表明將採取一系列並返回true/false,如果它是一個素數/非黃金,所以它看起來依稀像這樣:扭曲:使代碼非阻塞


def IsPrime(numberin): 
    for n in range(2,numberin): 
     if numberin % n == 0: return(False) 
    return(True) 

(剛說明)。

現在讓我們說有一個Web服務器需要根據提交的值調用IsPrime。大型numberin需要很長時間。

如果在此期間另一個用戶請求小數的素數,有沒有辦法使用reactor/deferreds體系結構異步運行這兩個函數調用,以便在計算結果之前返回short calc的結果長期計算?

我明白如何做到這一點,如果IsPrime功能來自其他網絡服務器,我的網絡服務器將做一個延期的getPage,但如果它只是一個本地功能?

即,可以以某種方式扭曲兩次調用IsPrime之間的時間共享,還是需要顯式調用新線程?

或者,IsPrime循環是否需要分成一系列較小的循環,以便控制可以快速傳回反應堆?

還是別的什麼?

+3

當你有一個真正需要很多時間的功能,例如。它是「阻塞」的,你可以使用'deferToThread()'在一個線程中運行它:http://twistedmatrix.com/documents/11.0.0/api/twisted.internet.threads.deferToThread.html – 2011-05-24 22:31:01

+1

或Twisted's Process支持:http://twistedmatrix.com/documents/current/core/howto/process.html – 2011-05-24 22:32:50

+0

哈,我寫了一個[非常類似的問題](http://stackoverflow.com/questions/5719782/confusion-about-cpu最近在Node.js中使用-intensive-code-in-node-js)。有興趣瞭解Twisted中的解決方案。 – YXD 2011-05-24 23:41:13

回答

26

我認爲你目前的理解基本上是正確的。 Twisted只是一個Python庫,您編寫的Python代碼使用它正常執行,正如您期望的Python代碼所示:如果您只有一個線程(和一個進程),那麼只有一件事發生。幾乎沒有Twisted提供的API創建新的線程或進程,因此在正常情況下,代碼會按順序運行; isPrime直到完成第一次執行後才能執行第二次。

Twisted仍然只考慮單個線程(和一個進程),所有Twisted的「併發」或「並行性」來自於阻塞網絡I/O(以及某些其他阻塞操作)的事實, Twisted提供了以非阻塞方式執行操作的工具。這可以讓程序繼續執行其他工作,否則它可能會一直等待阻塞I/O操作(例如讀取或寫入套接字)完成。

通過將事件分成小塊並讓事件處理程序在這些塊之間運行,可以使事物「異步」。如果轉換不會使代碼難以理解和維護,這有時是一種有用的方法。 Twisted提供了一個幫助安排這些大塊工作的幫助,cooperate。使用這個幫助器是有好處的,因爲它可以根據所有不同的工作來源做出調度決策,並確保有足夠的時間來爲事件源提供服務而不會產生額外的延遲(換言之,您添加的作業越多,每個工作得到的時間就越少,以便反應堆可以繼續工作)。

Twisted還提供了幾個處理線程和進程的API。如果不清楚如何將工作分解成塊,這些可能很有用。您可以使用deferToThread在線程池中運行(線程安全的!)函數。方便的是,這個API返回一個Deferred,它最終會以該函數的返回值觸發(或者如果函數引發異常,則返回Failure)。這些延遲看起來像其他任何東西,並且就使用它們的代碼而言,它也可以從類似getPage的調用返回 - 這是一個不使用額外線程的函數,只是非阻塞I/O和事件處理程序。

由於Python不適合在單個進程中運行多個CPU綁定線程,因此Twisted還提供了用於啓動和與子進程通信的非阻塞API。您可以將計算卸載到這些進程中,以充分利用更多的CPU或內核,而無需擔心GIL會讓您放慢速度,這是分塊策略和線程方法都不能提供的。處理這些進程的最低級別的API是reactor.spawnProcess。還有Ampoule,這個軟件包將爲您管理一個進程池,併爲進程提供deferToThread的模擬,deferToAMPProcess