2016-12-27 32 views
0

我有用於一次增量更新一個大的(> 1K)列表中的一個片一個defer.inlineCallback功能。這個列表隨時可能發生變化,並且由於這種行爲我得到了錯誤。循環通過改變數據集inlineCallbacks /產率(蟒加捻)

的我在做什麼,最簡單的表示是: -

@defer.inlineCallbacks 
def _get_details(self, dt=None): 
    data = self.data 
    for e in data: 
     if needs_update(e): 
      more_detail = yield get_more_detail(e) 
      do_the_update(e, more_detail) 
    schedule_future(self._get_details) 

self.data是最初使用在應用程序啓動的基本信息(如姓名和ID)的詞典列表。 _get_details將在反應堆允許的情況下運行,以獲取數據中每個項目的更詳細信息,隨着項目的進行更新項目。

這種運作良好,當self.data不會改變,但一旦它被改變(可以在任何點)的循環顯然指的是錯誤的信息。事實上,在這種情況下,最好是完全停止循環。

我能當數據被更改設置標誌在我的課(其中inlineCallback可以再檢查)。

  1. 應該在哪裏這張支票進行?
  2. 怎樣的inlineCallback代碼中執行比正常deferred(實際上是一個普通的Python發電機)。
  3. 是否執行代碼停止,每次遇到yield(即我可以依靠一個yield和下一個是原子之間的代碼)?
  4. 在不可靠的大名單的情況下,我應該甚至可以通過數據(for e in data)循環,或者是有沒有更好的辦法?

回答

0
@defer.inlineCallback 
def _get_details(self, dt=None): 
    data = self.data 
    i = 0 
    while i < len(data): 
     e = data[i] 
     if needs_update(e): 
      more_detail = yield get_more_detail(e) 
      if i < len(data) or data[i] != e: 
       break 
      do_the_update(e, more_detail) 
     i += 1 
    schedule_future(self._get_details) 

基於更多的測試,下面是我的看法。

  1. for e in data遍歷元素,該元素仍然存在,即使數據本身並沒有之前和在yield後聲明。

  2. 據我所知道的,執行是一個yield和未來之間的原子。

  3. 通過數據循環是更透明地通過使用一個計數器來完成。這也允許檢查數據是否已經改變。該檢查可以在yield之後的任何時候完成,因爲在返回yield之前必須發生任何更改。這導致上面顯示的代碼。

1

在執行過程中被扭曲反應器永遠不會搶佔你的代碼 - 你必須返回一個值自願屈服的反應器。這就是爲什麼編寫阻塞I/O的Twisted代碼是非常可怕的,因爲在等待磁盤時反應堆無法安排任何任務。

所以簡單的答案是,是的,執行是收益率之間的原子。

沒有@inlineCallbacks,該_get_details函數返回一個發電機。@inlineCallbacks註釋簡單地將生成器封裝在延遲中,該延遲遍歷生成器,直到它遇到StopIteration異常或defer.returnValue異常。當這些條件中的任何一個達到時,inlineCallbacks激發其延期。真的很聰明。

我不太瞭解您的用例來幫助解決併發問題。也許用tuple()創建一個副本並更新它。但似乎你真的想要一個事件驅動的解決方案,而不是一個由國家驅動的解決方案。

+0

謝謝,這是我的結論。關於用例,我的應用程序是事件驅動的,但由於更新既冗長又可分割,而且基於相對不穩定的數據,我正在採用盡力而爲的狀態驅動解決方案,它只是試圖更新任何需要更新,然後讓反應堆繼續。之前基於線程的「後臺更新」嘗試更糟,因爲更新幾乎可以保證在完成時出錯(對於當前數據集更新需要20-30秒)。 –

0

self.data是字典列表...一旦改變(可以在任何點)的循環顯然指的是錯誤的信息

如果您正在修改的列表,而你遍歷正如Raymond Hettinger會說的那樣「你生活在罪惡之國,你應該得到發生在你身上的一切。」 :)這樣的場景應該避免或列表應該是不可變的。爲了規避這個問題,您可以使用self.data.pop()DeferredQueue對象來存儲數據。這樣您就可以隨時添加和刪除元素而不會造成不良影響。與列表舉例:

@defer.inlineCallbacks 
def _get_details(self, dt=None): 
    try: 
     data = yield self.data.pop() 
    except IndexError: 
     schedule_future(self._get_details) 
     defer.returnValue(None)   # exit function 

    if needs_update(e): 
     more_detail = yield get_more_detail(data) 
     do_the_update(data, more_detail) 

    schedule_future(self._get_details) 

看看DeferredQueue因爲Deferred是當get()函數被調用返回,您可以鏈的回調來處理每個你從隊列流行元素。

+0

我不能從列表中彈出,因爲它會修改它。我知道這個問題在這個問題上還不太清楚,但是這個列表本意是爲了某種內存中的某種東西,我所問的功能是該信息的後臺更新(不是唯一的)。因此,爲什麼名單不可能是不可改變的,我也不能避免這種情況。 –

0

您需要保護對共享資源的訪問(self.data)。 你可以這樣做:twisted.internet.defer.DeferredLock

http://twistedmatrix.com/documents/current/api/twisted.internet.defer.DeferredLock.html

方法acquire

試圖獲取鎖。返回DeferredLock作爲值時發生的鎖定 採集。如果鎖定被鎖定,則 ,則延遲被放置在等待列表的末尾。

方法release

解除鎖定。如果有一個等待列表,則等待列表中的第一個Deferred將被回叫。

+0

這對我的情況有什麼幫助?我希望循環訪問數據,而不是鎖定它。 –