使用inlineCallbacks / yield循环更改数据集(python-twisted)

时间:2016-12-27 21:53:26

标签: python twisted deferred-execution

我有一个defer.inlineCallback功能,可以逐个逐步更新一个大的(> 1k)列表。此列表可能随时更改,并且因为该行为而导致出现错误。

我所做的最简单的表现是: -

@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和下一个for e in data之间依赖此代码吗?
  4. 对于不可靠的大型列表,我是否应该循环遍历数据(Directory Structure: - / - root - var - www - ... ),还是有更好的方法?

4 个答案:

答案 0 :(得分:1)

Twisted reactor在执行时永远不会抢占你的代码 - 你必须通过返回一个值来自愿屈服于反应堆。这就是编写阻塞I / O的Twisted代码是如此可怕的原因,因为在等待磁盘时,reactor无法安排任何任务。

所以简短的回答是,执行在收益率之间是原子的。

如果没有@inlineCallbacks,_get_details函数将返回一个生成器。 @inlineCallbacks注释只是将生成器包装在Deferred中,遍历生成器,直到它到达StopIteration异常或defer.returnValue异常。达到其中任何一个条件时,inlineCallbacks将触发其延迟。它非常聪明,真的。

我对您的用例了解不足以帮助解决并发问题。也许用tuple()复制列表并更新它。但似乎你真的想要一个事件驱动的解决方案,而不是一个由国家驱动的解决方案。

答案 1 :(得分: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和下一个yield之间是原子的。

  3. 使用计数器可以更加透明地完成数据循环。这还允许检查数据是否已经改变。可以在yield之后的任何时间进行检查,因为在server返回之前必须进行任何更改。这导致上面显示的代码。

答案 2 :(得分: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(),您可以链接回调来处理从队列中弹出的每个元素。

答案 3 :(得分:0)

您需要保护对共享资源(<h1>@TempData["quote"]</h1>// this prints out the correct value @model login.Models.Quotes <h1>Edit Your Quote</h1> @using(Html.BeginForm("EditQuote","Home")) { <p> <label>Your Quote</label> @Html.TextAreaFor(d=>d.quotes, new { @Value = @TempData["quote"]}) @Html.ValidationMessageFor(d => d.quotes) </p> <input type="submit" name="submit" value="Edit my quote!"/> } )的访问权限。 您可以执行以下操作:self.data

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

  

方法twisted.internet.defer.DeferredLock

     

尝试获取锁定。返回在锁定时触发的Deferred   使用DeferredLock作为值进行采集。如果锁被锁定,   然后,Deferred被放置在等待名单的末尾。

     

方法acquire

     

解锁。如果有一个等待列表,那么该等待列表中的第一个Deferred将被回调。