循环通过改变数据集inlineCallbacks /产率(蟒加捻)
我有用于一次增量更新一个大的(> 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
可以再检查)。
- 应该在哪里这张支票进行?
- 怎样的
inlineCallback
代码中执行比正常deferred
(实际上是一个普通的Python发电机)。 - 是否执行代码停止,每次遇到
yield
(即我可以依靠一个yield
和下一个是原子之间的代码)? - 在不可靠的大名单的情况下,我应该甚至可以通过数据(
for e in data
)循环,或者是有没有更好的办法?
@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
之前必须发生任何更改。这导致上面显示的代码。
在执行过程中被扭曲反应器永远不会抢占你的代码 - 你必须返回一个值自愿屈服的反应器。这就是为什么编写阻塞I/O的Twisted代码是非常可怕的,因为在等待磁盘时反应堆无法安排任何任务。
所以简单的答案是,是的,执行是收益率之间的原子。
没有@inlineCallbacks,该_get_details函数返回一个发电机。@inlineCallbacks注释简单地将生成器封装在延迟中,该延迟遍历生成器,直到它遇到StopIteration
异常或defer.returnValue
异常。当这些条件中的任何一个达到时,inlineCallbacks激发其延期。真的很聪明。
我不太了解您的用例来帮助解决并发问题。也许用tuple()创建一个副本并更新它。但似乎你真的想要一个事件驱动的解决方案,而不是一个由国家驱动的解决方案。
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()
函数被调用返回,您可以链的回调来处理每个你从队列流行元素。
我不能从列表中弹出,因为它会修改它。我知道这个问题在这个问题上还不太清楚,但是这个列表本意是为了某种内存中的某种东西,我所问的功能是该信息的后台更新(不是唯一的)。因此,为什么名单不可能是不可改变的,我也不能避免这种情况。 –
您需要保护对共享资源的访问(self.data
)。 你可以这样做:twisted.internet.defer.DeferredLock
。
http://twistedmatrix.com/documents/current/api/twisted.internet.defer.DeferredLock.html
方法
acquire
试图获取锁。返回DeferredLock作为值时发生的锁定 采集。如果锁定被锁定,则 ,则延迟被放置在等待列表的末尾。
方法
release
解除锁定。如果有一个等待列表,则等待列表中的第一个Deferred将被回叫。
这对我的情况有什么帮助?我希望循环访问数据,而不是锁定它。 –
谢谢,这是我的结论。关于用例,我的应用程序是事件驱动的,但由于更新既冗长又可分割,而且基于相对不稳定的数据,我正在采用尽力而为的状态驱动解决方案,它只是试图更新任何需要更新,然后让反应堆继续。之前基于线程的“后台更新”尝试更糟,因为更新几乎可以保证在完成时出错(对于当前数据集更新需要20-30秒)。 –