Python 中断队列。获取
如何在Python3.X中中断阻塞Python 中断队列。获取,python,python-3.x,Python,Python 3.x,如何在Python3.X中中断阻塞队列.get() 在Python2.X中似乎可以工作,但在Python3.5中却不能这样说 运行在Windows7、CPython3.5.1、64位机器和Python上。 看起来它在Ubuntu上的表现不一样。它在Python 2上工作的原因是队列。在Python 2上带超时的get实现得非常糟糕;Python2实际上没有一个支持定时阻塞获取的锁原语(这是队列内部条件变量需要的,但是缺少,因此它使用繁忙循环)。当您在Python2上尝试这一点时,您要检查的是Ct
队列.get()
在Python2.X中似乎可以工作,但在Python3.5中却不能这样说
运行在Windows7、CPython3.5.1、64位机器和Python上。
看起来它在Ubuntu上的表现不一样。它在Python 2上工作的原因是
队列。在Python 2上带超时的get
实现得非常糟糕;Python2实际上没有一个支持定时阻塞获取的锁原语(这是队列
内部条件
变量需要的,但是缺少,因此它使用繁忙循环)。当您在Python2上尝试这一点时,您要检查的是Ctrl-C
是否在一个(短的)时间后被处理。sleep
调用完成,并且,它非常短,即使在新的睡眠开始时按Ctrl-C,您可能也不会注意到
Python3提供了真正的定时锁获取支持(这要归功于将目标操作系统的数量缩小到那些具有本机定时互斥或某种信号量的操作系统)。因此,您实际上在整个超时期间阻塞锁获取,而不是在轮询尝试之间阻塞0.05秒
看起来Windows允许注册Ctrl-C的处理程序,这意味着不会中断锁获取来处理它。当定时锁获取最终失败时,Python会被告知Ctrl-C
,因此如果超时很短,您最终会看到键盘中断
,但直到超时结束后才会看到它。由于Python2Condition
一次只休眠0.05秒(或更少),因此Ctrl-C总是被快速处理,但Python3将一直休眠,直到获得锁为止
Ctrl-Break
保证作为一个信号,但Python也不能正确处理它(它只是终止进程),这可能不是您想要的
如果希望Ctrl-C
工作,您在某种程度上会被困在轮询中,但至少(与Python 2不同)您可以有效地轮询Ctrl-C
,同时在剩余时间对队列进行实时阻塞(因此,您会收到一个项目立即变为空闲的警报,这是常见情况)
导入时间
导入队列
def get_timed_可中断(q,超时):
stoploop=time.monotonic()+超时-1
而time.monotonic()
这会一次阻塞一秒钟,直到:
空
正常传播)Ctrl-C
在一秒钟的时间间隔内被按下(在该秒钟的剩余时间过后,KeyboardInterrupt
被触发)Ctrl-C
,它也将在此时升起)正如上面提供的伟大答案@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
只需使用不会阻塞的
get\u nowait
导入时间
...
尽管如此:
如果不是q.empty():
q、 获取_nowait()
打破
时间。睡眠(1)#可选超时
这显然是在忙着等待,但是
q.get()
基本上做了相同的事情。Dunno,这就是为什么我要问这个问题,它在Python 3中工作,例如,python3-c“导入队列;queue.queue().get()”
在我的Ubuntu机器上被Ctrl+c
成功中断。你确定你使用的是python3可执行文件,而不是python2吗?在我的windows上Ctrl+C
不会中断它应该不会Ctrl+Break
在windows上工作?@ChrisPCtrl+Break
与Ctrl+C
不同。请参阅两个注意事项-首先,队列上的1秒超时。get()
调用将对用户产生明显的延迟,我建议使用更短的时间。第二,保持stoptime
和remaining
违反-只需使用while time.monotonic()
和remaineremaining
@SteveCohen:我已调整以消除附加变量(并减少每循环算法);我不能直接使用time.monotonic()
,因为我希望循环在剩下不到一秒钟时停止。至于延误;嗯。一秒钟的延迟取消节目几乎没有意义;如果Ctrl-C要取消一个任务,但要继续运行,并且必须有响应,那么可以调整超时时间,但我正在针对常见情况进行优化;通常情况下,Ctrl-C很少(成本从未支付),阻塞是半常见的(更多的循环会增加常见情况的成本)。当然,如果超时时间小于1,您仍然会遇到这样的问题:queue.get()
。这真的很糟糕,因为队列.get()
应该至少发生一次。如果使用更短的超时,可以忽略(微不足道的)溢出情况并简化整个过程。正如您在回复中提到的,Python 2在0.05秒(ish)时进行了轮询。@SteveCohen:它在最后的get
中执行(尽管我必须修正计算以确保它在竞争条件下不会低于0,Queue.get
不会像我想象的那样将<0的超时视为0),即使在运行不到一秒的情况下,只有循环中忽略queue.Empty
的循环才会被跳过。0.05秒的轮询循环太短;我在工作项目中遇到过一些问题,这些问题假设生产者-消费者链中的许多线程(每个线程为下一个线程提供数据)可以使用定时阻塞等待,但最终的结果是Py2中的大量GIL争用;他们会使用1.4-1.8个核,做0.6-0.8个核的工作
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
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=max(0, min(1, timeout - time.monotonic())))
except queue.Empty:
if time.monotonic() > timeout:
raise