我有一个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
可以检查),当数据发生变化时。
inlineCallback
代码与普通deferred
(以及普通的python生成器)相比如何执行。yield
时代码执行是否都会停止(即我可以在一个yield
和下一个for e in data
之间依赖此代码吗?Directory Structure:
- /
- root
- var
- www
- ...
),还是有更好的方法?答案 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)
根据更多测试,以下是我的观察结果。
for e in data
遍历元素,在yield
语句之前和之后,即使数据本身不存在,元素仍然存在。
据我所知,执行在一个yield
和下一个yield
之间是原子的。
使用计数器可以更加透明地完成数据循环。这还允许检查数据是否已经改变。可以在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将被回调。