Python gevent/线程导致一些死锁

Python gevent/线程导致一些死锁,python,multithreading,deadlock,gevent,Python,Multithreading,Deadlock,Gevent,我有这段代码,其目的是消除重复请求 def dedup_requests(f): pending = {} @functools.wraps(f) def wrapped(*args, **kwargs): key = _make_call_key(args, kwargs) if key not in pending: pending[key] = gevent.spawn(f, *args, **kwargs)

我有这段代码,其目的是消除重复请求

def dedup_requests(f):
    pending = {}

    @functools.wraps(f)
    def wrapped(*args, **kwargs):
        key = _make_call_key(args, kwargs)
        if key not in pending:
            pending[key] = gevent.spawn(f, *args, **kwargs)
        result = pending[key].get()
        if key in pending:
            del pending[key]
        return result

    return wrapped
我怀疑这是某种原因造成了僵局(这种情况偶尔发生一次,我无法重现)

使用线程和gevent时都会发生这种情况

是否允许反复使用
get

当不涉及线程时,这段代码甚至会产生死锁吗


请注意,它在其他gevent任务下运行,因此衍生任务可能会衍生其他任务,以防出现问题。

您的问题是代码不异步。您需要让函数本身处理密钥更新,然后在while循环中测试您的值。这是异步工作的一个例子。您可以通过注意最后一个元素有时在列表中首先出现来证明这一点

import gevent
import random

pending = {}
def dedup_requests(key, *args, **kwargs):
    global pending
    if key not in pending:
        gevent.spawn(ftest, key, *args, **kwargs)

def ftest(key, *args, **kwargs):
    global pending
    z = random.randint(1,7)
    gevent.sleep(z)
    pending[key] = z
    return z

l = ['test','test2','test3']
for i in l:
    dedup_requests(i)

while 1:
    if set(pending.keys()) != set(l):
        print(pending)
    else:
        print(pending)
        break
    gevent.sleep(1)

虽然我仍然不完全理解死锁的来源(我最好的猜测是,
get
在多次调用时并没有像预期的那样工作),但这似乎是可行的:

from gevent import lock

def queue_identical_calls(f, max_size=100):
    pending = {}

    @functools.wraps(f)
    def wrapped(*args, **kwargs):
        key = _make_call_key(args, kwargs)
        if key not in pending:
            pending[key] = lock.BoundedSemaphore(1)
        lock_for_current_call = pending[key]
        lock_for_current_call.acquire()
        result = f(*args, **kwargs)
        lock_for_current_call.release()

        if len(pending) > max_size:
            pending.clear()

        return result

    return wrapped

谢谢,但这似乎不能解决同样的问题。关键是阻止多个“线程”同时请求的相同工作(想象一下,在同一秒内多次使用同一字符串调用)。在您的解决方案中,睡眠(我假设是模拟网络)会发生很多次。您的解决方案本质上是记忆。set函数为您消除重复。睡眠允许应用程序在绿叶中移动,
Sleep(0)
将允许您实时移动。IIUC
set
没有足够早地消除重复,也没有使用字典。为了更好地理解我的意思,在
f\u test
中添加一行表示网络请求的代码(例如
remote\u procedure\u call()
),通过遵循代码,您将看到这一行将被
l
中的每个元素调用,而不考虑重复。我刚才提供了异步的示例。在ftest中,您将在运行函数之前检查该键,如果该键存在,您将取消。我认为您下面的答案破坏了异步进程,并将其转换回单线程阻塞进程。