任意睡眠在线程等待超时

时间:2014-03-03 12:06:33

标签: python python-2.7 sleep python-multithreading

在我开始描述我的问题之前,值得一提的是我正在使用Python 2.7。我没有检查过,但这可能与Python 3.x无关。

在使用Python的Queues时,我发现了一些奇怪的东西。通常,当从队列中获取一个对象时,我允许长但有限的超时(例如几秒),以便在没有找到对象的情况下允许调试和错误报告。我发现的是,有时在将对象插入先前空的队列的时间与同一个队列的get方法返回该对象的时间之间存在奇怪的差距,即使在为该对象调用put之前调用了该方法。

稍微挖掘一下,我发现间隙是由睡觉填补的。在Queue模块中,如果传递给timeout方法的get参数不是None,并且是正数,则non_empty {{3使用正参数调用wait方法(不是100%精确;事实上,Queue的“_qsize”方法,它返回的长度为底层deque首先被验证为返回0,但只要队列在第一个位置为空,接下来就是条件的等待。

如果Conditions的{​​{1}}方法超时,则wait方法的行为会有所不同。如果没有超时,则只需调用waiter.acquire。这是在C中定义的,超出了我的理解范围,但似乎它可以正常工作。但是,如果给出超时 ,则会出现奇怪的睡眠序列,此时睡眠时间以任意大小(1毫秒)开始,并随着时间的推移而变长。以下是运行的确切代码:

# 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)

这显然是我在新对象放入先前空的队列之间发现的差距以及已经调用的get方法返回该对象的时间之间的差距的原因。随着延迟时间呈指数级增长,直到被一个巨大的(从我的角度来看)0.05秒的大小阻挡,它会在我的应用程序生命中产生令人惊讶和不必要的重要睡眠。

你能解释一下这个的目的是什么吗? Python开发人员是否认为没有Python用户会关心这样的时间长度?有快速的解决方法或正确的解决方案吗?你建议我过载螺纹模块吗?

2 个答案:

答案 0 :(得分:5)

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

太糟糕了。


  

你能解释一下这个的目的是什么吗? Python开发人员是否认为没有Python用户会关心这样的时间长度?

打败我......


  

您是否建议我重载线程模块?

重载线程模块,或者迁移到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 :(得分:0)

你可以做些什么来确保Queue没有做一些奇怪的事情是使用方法get_nowait和Exception Empty。看看我在生产服务器中的这些行。 (当然修改为适合这个例子)。

from Queue import Queue, Empty

while receiver.isAlive:
    try:
        rec = Record(queue.get_nowait())
    except Empty:
        # Set someTime with the value you want
        someTime = 0.1
        sleep(someTime)
    else:
        doSomething(rec)

另外,请记住以下内容:

  
    

time.sleep()函数使用底层操作系统的sleep()函数。最终这个功能有局限性。例如,在标准Windows安装上,您可以睡眠的最小间隔为10 - 13毫秒。 Linux内核往往具有更高的滴答速率,其中间隔通常接近1毫秒。