如何在Python 3.X中断阻塞Queue.get()
?
在Python 2.X中setting a long timeout似乎有效,但对于Python 3.5来说也是如此。
在Windows 7,CPython 3.5.1,64位机器和Python上运行。 看起来它在Ubuntu上的行为并不相同。
答案 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
一次阻止一秒钟直到:
Ctrl-C
正常传播)KeyboardInterrupt
在一秒间隔期间被按下(在第二个时间段的剩余时间之后,Ctrl-C
被提升)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()
基本上做了同样的事情。