2016-12-27 33 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

这对我的情况有什么帮助?我希望循环访问数据,而不是锁定它。 –