2013-03-25 34 views
4

目前我仍然是一個初學者在扭曲,這使我煩惱。按順序排列多個被打斷的敵人

我通過TCP發出一系列命令,並等待來自lineRecieved閱讀器的響應,這可能需要幾秒鐘的時間來處理併到達,所以我將它包裹在一個戰鬥中。第一個延期工作正常,但第二個工作正常,因爲第一個工作仍在處理中,導致垃圾,因爲端點一次只能處理一個命令。在asysc系統中預期的行爲,但不是我需要發生的事情。如果我有一個或兩個命令,我可以使用一個deferedChain進行處理,但是由於我有可能有數十個命令按順序運行,所以我擔心這會很快變成難以維護的意大利麪條。

乾淨的方法是什麼?

非常感謝

示例代碼

def connectionMade(self): 
    self.fire_def('command1') 
    print'fire command 2' 
    self.fire_def('command2')#Fires when command one is running 

def fire_def(self,request): 
    d = self.getInfo(request) 
    d.addCallback(self.print_result) 
    return d 

def print_result(result): 
    print result 


def getInfo(self,request): 
    print 'sending', request 
    self.d = defer.Deferred() 
    self.sendLine(request) 
    return self.d 

def lineReceived(self, line): 
    line = line.strip() 
    self.buffer.append(line) 
    if self.d is None: 
     return 
    if 'result_I_want' in self.buffer: 
     print 'Firing Callback' 
     self.d.callback(self.buffer) 

回答

5

您問題中的代碼只知道如何跟蹤一個Deferred。如果應用程序代碼調用getInfo兩次沒有足夠的間隔時間爲第一個動作完成與結果,那麼它會破壞自己的內部跟蹤狀態:

def getInfo(self,request): 
    print 'sending', request 
    self.d = defer.Deferred() 
    self.sendLine(request) 
    return self.d 

d_foo = getInfo(foo) 
d_bar = getInfo(bar) 

在這個序列中,d_food_bar不同Deferred實例。但是,在第二次調用getInfo時,屬性self.d的值從d_foo更改爲d_bard_fooDeferred丟失。後來,當`lineReceived運行:

def lineReceived(self, line): 
    line = line.strip() 
    self.buffer.append(line) 
    if self.d is None: 
     return 
    if 'result_I_want' in self.buffer: 
     print 'Firing Callback' 
     self.d.callback(self.buffer) 

self.dd_bar即使線可能是在FOO請求的響應。這意味着d_bar將得到foo的響應d_foo永遠不會得到任何響應。

要解決此問題,可能有助於在協議上保留Deferred實例的列表(或隊列)。在發出新的信息請求時附加到它,當收到響應時從它的前面彈出。 (我不確定你正在執行什麼協議,所以我不知道你將如何決定有多少行可以構成響應。如果協議沒有定義這個,那麼它會被破壞,你可能想要切換到更好的協議。)

如果您修復此問題,那麼響應至少會傳遞到不同的Deferred實例。

您還描述了與強制順序操作有關的問題。有幾種方法可以解釋這一點。一種方法是將其解釋爲意味着您只希望一次請求在網絡上「突出」。換句話說,你不希望getInfo發送新的請求行,直到後lineReceived交付響應數據由以前調用getInfo返回Deferred

在這種情況下,遞延鏈接只是事情。儘管事實上你有N個Deferreds,但當你施加這個順序限制時,實際上你有一系列2個Deferreds。您有延遲運行較早和延遲,應該只在較早的結果後運行。您將此延伸至N,然後再考慮後面的延遲爲新對中的較早延遲,並且第三個延遲變爲新的延遲。

或者換一種說法,如果你有D1,D2,D3和D4,然後你把它們連這樣的:

D2 is chained to D1 and only runs when D1 is complete 
D3 is chained to D2 and only runs when D2 is complete 
D4 is chained to D3 and only runs when D3 is complete 

不過,雖然這可以工作,它實際上沒有實現序列化的最簡單方法。相反,我建議您明確地排隊工作getInfo並明確unqueueing它lineReceived

def _sendRequest(self, d, request): 
    print 'sending', request 
    self.d = d 
    self.sendLine(request) 

def getInfo(self,request): 
    if self.d is None: 
     d = defer.Deferred() 
     self._sendRequest(d, request) 
     return d 
    else: 
     queued_d = defer.Deferred() 
     self._requests.append((request, queued_d)) 
     return queued_d 


def lineReceived(self, line): 
    line = line.strip() 
    self.buffer.append(line) 
    if self.d is None: 
     return 
    if 'result_I_want' in self.buffer: 
     print 'Firing Callback' 
     now_d = self.d 
     self.d = None 
     buffer = self.buffer 
     self.buffer = [] 
     if self._requests: 
      request, queued_d = self._requests.pop(0) 
      self._sendRequest(queued_d, request) 
     now_d.callback(buffer) 

請注意,在lineReceived的代碼需要把一切都達成一致的狀態now_d.callback(buffer)前行。這是一個微妙而重要的點。可以存在於now_d回調其影響的方案 - 例如,通過再次調用getInfo。在使代碼運行之前,協議處於一致狀態非常重要,否則它會變得混亂 - 可能是因爲發送請求失序,或者在實際發送請求時排隊請求。這是使代碼安全的示例,其針對重入。這不是Twisted使用程序所特有的想法,但是由於人們經常將再入侵的想法與線程化程序聯繫起來,人們在編寫基於Twisted的代碼時往往會忽視這種可能性。

+0

我曾經得到過讓·保羅最清楚最全面的答案之一,非常感謝。 – Marcus1219 2013-03-26 15:13:51

2

基本上,如果你想讓他們一個接一個執行返回彼此deferreds。

所以你希望只有在d1完成後才能執行d2,那麼請從d1的回調中返回d2。

換句話說,根據您的示例,您需要在command1的回調結束附近的某處調用command2。

+0

謝謝,我知道這是基本的想法,但如果延遲執行20失敗,該模式將獲得巨大的混亂處理defereds,其中一些將有對方,如依賴的N個,21火其他22個etc.Glyph狀態你阻止一個戰鬥是一個不好的模式,但是我想知道什麼是正確的模式來執行一個同步命令鏈扭曲的方式。 Eeek! – Marcus1219 2013-03-26 11:01:01

+1

Eeek!延期失敗時不會出現錯誤嗎?否則,它不會比協議處理程序所在的狀態機更復雜。只是,而不是語句S1,S2,S3 ....你通常寫一個接一個,讓翻譯管理控制流程,你寫S1返回推遲到火與S2返回與S3作爲其回調推遲,並讓反應堆控制你的流量。如果您認爲它太扭曲而無法閱讀,則可以使用內聯回放。 – 2013-03-26 21:42:35