更好的解决方案,用于Python Threading.Event半忙等待

时间:2012-03-14 19:41:43

标签: python multithreading events busy-loop

我使用非常标准的Threading.Event: 主线程在一个循环中运行:

event.wait(60)

请求中的其他阻止,直到回复可用,然后启动a:

event.set()

我希望主线程选择40秒,但事实并非如此。 从Python 2.7源代码Lib / threading.py:

# Balancing act:  We can't afford a pure busy loop, so we
# have to sleep; but if we sleep the whole timeout time,
# we'll be unresponsive.  The scheme here sleeps very
# little at first, longer as time goes on, but never longer
# than 20 times per second (or the timeout time remaining).
endtime = _time() + timeout
delay = 0.0005 # 500 us -> initial delay of 1 ms
while True:
   gotit = waiter.acquire(0)
   if gotit:
       break
   remaining = endtime - _time()
   if remaining <= 0:
       break
   delay = min(delay * 2, remaining, .05)
   _sleep(delay)

我们得到的是每500us选择一次系统调用。 这会导致机器上出现明显的负载,选择循环非常紧张。

有人可以解释为什么涉及平衡行为,为什么它与等待文件描述符的线程不同。

第二,有没有更好的方法来实现一个大多数没有这么紧的循环的主线程?

2 个答案:

答案 0 :(得分:3)

我最近遇到了同样的问题,我还在threading模块中跟踪了这个确切的代码块。

太糟糕了。

解决方案是重载线程模块,或迁移到python3,这部分实现已经修复。

在我的情况下,迁移到python3本来是一项巨大的努力,所以我选择了前者。我做的是:

  1. 我创建了一个快速.so文件(使用cython),其中包含pthread的接口。它包括调用相应pthread_mutex_*函数的python函数,以及针对libpthread的链接。具体而言,与我们感兴趣的任务最相关的功能是pthread_mutex_timedlock
  2. 我创建了一个新的threading2模块,(并用import threading替换了我的代码库中的所有import threading2行。在threading2中,我重新定义了threadingLockConditionEvent)以及来自Queue的所有相关类别我经常使用(QueuePriorityQueue)。 Lock类完全使用pthread_mutex_*函数重新实现,但其余的更容易 - 我只是将原始子类(例如threading.Event)子类化,并覆盖__init__到创建我的新Lock类型。其余的工作正常。
  3. Lock类型的实现与threading中的原始实现非常相似,但我基于acquire的新实现基于python3中找到的代码} threading模块(当然,它比上述“平衡行为”块简单得多)。这部分相当简单。

    (顺便说一句,我的案例结果是我的大规模多线程进程加速了30%。甚至超出了我的预期。)

答案 1 :(得分:2)

我完全同意你的意见,这很蹩脚。

目前,我正在坚持一个简单的选择调用,没有超时,并且正在监听之前创建的管道。 通过在管道中写一个字符来完成唤醒。

请参阅gunicorn中的sleepwakeup函数。