Python Twisted';延迟锁

Python Twisted';延迟锁,python,twisted,Python,Twisted,是否有人能提供一个例子,并解释何时以及如何使用Twisted's 我有一个延迟队列,我想我有一个我想要防止的竞争条件,但我不确定如何将两者结合起来。当您有一个异步的关键部分,需要防止重叠(可以说是“并发”)执行时,请使用延迟锁 以下是此类异步关键部分的示例: class NetworkCounter(object): def __init__(self): self._count = 0 def next(self): self._count +

是否有人能提供一个例子,并解释何时以及如何使用Twisted's


我有一个延迟队列,我想我有一个我想要防止的竞争条件,但我不确定如何将两者结合起来。

当您有一个异步的关键部分,需要防止重叠(可以说是“并发”)执行时,请使用
延迟锁

以下是此类异步关键部分的示例:

class NetworkCounter(object):
    def __init__(self):
        self._count = 0

    def next(self):
        self._count += 1
        recording = self._record(self._count)
        def recorded(ignored):
            return self._count
        recording.addCallback(recorded)
        return recording

    def _record(self, value):
        return http.GET(
            b"http://example.com/record-count?value=%d" % (value,))
查看两次同时使用
next
方法将如何产生“损坏”结果:

给出了结果:

2 d1
2 d2
这是因为对
NetworkCounter.next
的第二次调用在对该方法的第一次调用使用
\u count
属性生成结果之前开始。这两个操作共享一个属性,并因此产生不正确的输出

使用
delferredlock
实例可以防止第二个操作从开始到第一个操作完成,从而解决此问题。您可以这样使用它:

class NetworkCounter(object):
    def __init__(self):
        self._count = 0
        self._lock = DeferredLock()

    def next(self):
        return self._lock.run(self._next)

    def _next(self):
        self._count += 1
        recording = self._record(self._count)
        def recorded(ignored):
            return self._count
        recording.addCallback(recorded)
        return recording

    def _record(self, value):
        return http.GET(
            b"http://example.com/record-count?value=%d" % (value,))
首先,请注意,
NetworkCounter
实例创建自己的
DeferredLock
实例。
DeferredLock
的每个实例都是不同的,并且独立于任何其他实例运行。任何参与使用关键部分的代码都需要使用相同的
DeferredLock
实例才能保护该关键部分。如果两个
NetworkCounter
实例以某种方式共享了状态,那么它们还需要共享一个
DeferredLock
实例,而不是创建自己的私有实例

接下来,请参见如何使用
DeferredLock.run
调用新的
\u Next
方法(所有应用程序逻辑都已移动到该方法中)
NetworkCounter
(或者使用
NetworkCounter
的应用程序代码)不调用包含关键部分的方法<代码>延迟锁定
负责执行此操作。这就是
delferredlock
如何防止关键部分被多个操作“同时”运行。在内部,
DeferredLock
将跟踪操作是否已开始和尚未完成。但是,只有当操作的完成被表示为延迟时,它才能跟踪操作的完成情况。如果您熟悉
Deferred
s,您可能已经猜到本例中(假设的)HTTP客户端API
HTTP.GET
,正在返回一个
Deferred
,该API在HTTP请求完成时触发。如果你还不熟悉它们,你应该现在就去读一读

一旦<代码>延迟<代码>,表示操作的结果-换句话说,一旦操作完成,<代码> DEFRESROLKON/CODE>将考虑关键部分“不使用”,并允许另一个操作开始执行它。它将通过检查是否有任何代码试图在关键部分使用时进入关键部分来完成此操作,如果有,它将运行该操作的函数

第三,请注意,为了序列化对关键部分的访问,
DeferredLock.run
必须返回一个
Deferred
。如果关键部分正在使用,并且调用了
DeferredLock.run
,则无法启动另一个操作。因此,它会创建并返回一个新的
延迟的
。当关键部分停止使用时,可以开始下一个操作,当该操作完成时,
DeferredLock.run
调用返回的
Deferred
将获得其结果。这一切对于任何已经期待
延迟的用户来说都是相当透明的-这只是意味着操作似乎需要更长的时间才能完成(尽管事实是,完成这项工作可能需要同样的时间,但需要等待一段时间才能开始,但对挂钟的影响是一样的)

当然,通过首先不共享状态,您可以比所有这一切更轻松地实现并发使用安全
NetworkCounter

class NetworkCounter(object):
    def __init__(self):
        self._count = 0

    def next(self):
        self._count += 1
        result = self._count
        recording = self._record(self._count)
        def recorded(ignored):
            return result
        recording.addCallback(recorded)
        return recording

    def _record(self, value):
        return http.GET(
            b"http://example.com/record-count?value=%d" % (value,))
此版本将
NetworkCounter使用的状态移动。下一步
为调用者从实例字典(即,它不再是
NetworkCounter
实例的属性)中生成有意义的结果,并进入调用堆栈(即,它现在是一个与实现方法调用的实际帧关联的closed over变量)。由于每个调用都会创建一个新帧和一个新闭包,因此并发调用现在是独立的,不需要任何类型的锁定

最后,请注意,即使此修改版本的
NetworkCounter.next
仍使用
self.\u count
,它在单个
NetworkCounter
实例上的
next
调用中共享,但在同时使用时,这不会对实现造成任何问题系统,例如一个主要用于扭曲的系统,在函数或操作的中间没有上下文切换。在一个操作之间不能有一个上下文切换,在<代码>自我>。它们可以避免重新进入或并发导致的损坏


最后两点——通过避免共享状态和函数中代码的原子性来避免并发错误——结合起来意味着
delferredlock
通常不是特别有用。作为一个数据点,在我当前工作项目中大约75 KLOC(基于严重扭曲),没有使用
DeferredLock

nice,甚至不知道
DeferredLock
的存在。这对我来说可能很有用,我最终不得不自己实现它
class NetworkCounter(object):
    def __init__(self):
        self._count = 0

    def next(self):
        self._count += 1
        result = self._count
        recording = self._record(self._count)
        def recorded(ignored):
            return result
        recording.addCallback(recorded)
        return recording

    def _record(self, value):
        return http.GET(
            b"http://example.com/record-count?value=%d" % (value,))