中断Queue.get

时间:2016-04-23 22:12:18

标签: python python-3.x

如何在Python 3.X中断阻塞Queue.get()

在Python 2.X中setting a long timeout似乎有效,但对于Python 3.5来说也是如此。

在Windows 7,CPython 3.5.1,64位机器和Python上运行。 看起来它在Ubuntu上的行为并不相同。

3 个答案:

答案 0 :(得分:7)

它在Python 2上工作的原因是,在Python 2上超时的Queue.get实现得非常糟糕,as a polling loop with increasing sleeps between non-blocking attempts to acquire the underlying lock; Python 2实际上并没有支持定时阻塞获取的锁原语(这是Queue内部Condition变量需要但缺少的原因,因此它使用busy循环)。当你在Python 2上尝试这个时,所有你要检查的是Ctrl-C调用完成之后time.sleep调用之后是否处理Ctrl-C,而the longest sleep in Condition is only 0.05 seconds是{}简而言之,即使您在新睡眠开始的瞬间按下Ctrl-C,您也可能不会注意到。

Python 3具有真正的定时锁定获取支持(由于将目标操作系统的数量缩小到具有本机定时互斥锁或某种信号量的那些操作系统的数量)。因此,您实际上在整个超时期间阻止锁定获取,而不是在轮询尝试之间一次阻止0.05秒。

看起来Windows允许为Ctrl-C注册表示Ctrl-C doesn't necessarily generate a true signal的处理程序,因此锁定获取不会被中断以处理它。当定时锁定获取最终失败时,Python会被告知KeyboardInterrupt,因此如果超时很短,您最终会看到Condition,但在超时失效之前不会看到它。由于Python 2 Ctrl-Break一次只能休息0.05秒(或更少),因此总是快速处理Ctrl-C,但Python 3将一直睡眠直到获得锁定。

Ctrl-C保证表现为一个信号,但它也不能被Python正确处理(它只会杀死进程),这可能不是你想要的。

如果你想让Ctrl-C工作,你就会在某种程度上陷入轮询,但至少(与Python 2不同)你可以有效地轮询import time import queue def get_timed_interruptable(q, timeout): stoploop = time.monotonic() + timeout - 1 while time.monotonic() < stoploop: try: return q.get(timeout=1) # Allow check for Ctrl-C every second except queue.Empty: pass # Final wait for last fraction of a second return q.get(timeout=max(0, stoploop + 1 - time.monotonic())) ,同时在队列上实时阻止剩下的时间(因此您会立即通知某个项目免费,这是常见的情况)。

Empty

一次阻止一秒钟直到:

  1. 剩余时间不到一秒(它会阻止剩余时间,然后允许Ctrl-C正常传播)
  2. KeyboardInterrupt在一秒间隔期间被按下(在第二个时间段的剩余时间之后,Ctrl-C被提升)
  3. 获取一个项目(如果按下了render(){ <View> <Text style={styles.welcome} ref={component => this._text = component}> Some Text </Text> <TouchableHighlight underlayColor='#88D4F5' style={styles.button}> <View> <Text style={styles.buttonText} onPress={this.useNativePropsToUpdate.bind(this)}> Iam the Child </Text> </View> </TouchableHighlight> </View> } ,此时它也会升起)

答案 1 :(得分:1)

正如上面提供的@ShadowRanger的评论主题中所提到的,这是他职能的另一种简化形式:

import queue


def get_timed_interruptable(in_queue, timeout):
    '''                                                                         
    Perform a queue.get() with a short timeout to avoid                         
    blocking SIGINT on Windows.                                                 
    '''
    while True:
        try:
            # Allow check for Ctrl-C every second                               
            return in_queue.get(timeout=min(1, timeout))
        except queue.Empty:
            if timeout < 1:
                raise
            else:
                timeout -= 1

正如@Bharel在评论中指出的那样,这可能会比绝对超时运行几毫秒,这可能是不可取的。因此,这里的版本具有明显更好的精度:

import time
import queue


def get_timed_interruptable_precise(in_queue, timeout):
    '''                                                                         
    Perform a queue.get() with a short timeout to avoid                         
    blocking SIGINT on Windows.  Track the time closely
    for high precision on the timeout.                                                 
    '''
    timeout += time.monotonic()
    while True:
        try:
            # Allow check for Ctrl-C every second                               
            return in_queue.get(timeout=min(1, timeout - time.monotonic()))
        except queue.Empty:
            if time.monotonic() > timeout:
                raise
            else:
                pass

答案 2 :(得分:-1)

只需使用不会阻塞的 get_nowait

import time
...

while True:
  if not q.empty():
    q.get_nowait()
    break
  time.sleep(1) # optional timeout

这显然是在忙着等待,但 q.get() 基本上做了同样的事情。