python picamera, keyboard ctrl+c/sigint 没抓到
python picamera, keyboard ctrl+c/sigint not caught
我取自pycamera docs的例子进行快速捕获和处理,并添加了一个sigint事件处理程序来捕获键盘中断:
import io
import time
import threading
import picamera
# Create a pool of image processors
done = False
lock = threading.Lock()
pool = []
def signal_handler(signal, frame):
global done
print 'You pressed Ctrl+C!'
done=True
sys.exit()
signal.signal(signal.SIGINT, signal_handler)
class ImageProcessor(threading.Thread):
def __init__(self):
super(ImageProcessor, self).__init__()
self.stream = io.BytesIO()
self.event = threading.Event()
self.terminated = False
self.daemon=True;
self.start()
def run(self):
# This method runs in a separate thread
global done
while not self.terminated:
# Wait for an image to be written to the stream
if self.event.wait(1):
try:
self.stream.seek(0)
# Read the image and do some processing on it
#Image.open(self.stream)
#...
#...
# Set done to True if you want the script to terminate
# at some point
#done=True
finally:
# Reset the stream and event
self.stream.seek(0)
self.stream.truncate()
self.event.clear()
# Return ourselves to the pool
with lock:
pool.append(self)
def streams():
while not done:
with lock:
if pool:
processor = pool.pop()
else:
processor = None
if processor:
yield processor.stream
processor.event.set()
else:
# When the pool is starved, wait a while for it to refill
time.sleep(0.1)
with picamera.PiCamera() as camera:
pool = [ImageProcessor() for i in range(4)]
camera.resolution = (640, 480)
camera.framerate = 30
camera.start_preview()
time.sleep(2)
camera.capture_sequence(streams(), use_video_port=True)
# Shut down the processors in an orderly fashion
while pool:
with lock:
processor = pool.pop()
processor.terminated = True
processor.join()
但从未捕获到中断信号。
在 camera.capture_sequence(streams(), use_video_port=True)
运行之前捕获信号,在 capture_sequence
启动后不会调用信号处理程序。
我是 python 的新手,所以也许答案很简单。我在这里做错了什么?
编辑:
如果我删除以下代码,信号就会被捕获:
yield processor.stream
在您的代码中,done
变量是一个全局变量。
所以,每当你想在函数内部修改它时,你都需要使用关键字global
,否则它就变成了一个局部变量。
您应该像这样修复您的代码:
import signal
import sys
done = False
def signal_handler(signal, frame):
global done
print('You pressed Ctrl+C!')
done = True
sys.exit()
signal.signal(signal.SIGINT, signal_handler)
问题在于您正在使用 thread.join()
,它会阻塞主线程,这意味着您的程序必须等到您加入的那个线程结束才能继续。
信号总是会被主进程捕获,因为它是接收信号的进程,它是具有线程的进程。
关于如何处理主线程和CTRL+C有很多答案,我给你三个选择,
首先,为 join() 调用添加超时:
thread1.join(60)
详情 here
其次,启动一个新的进程来处理杀死程序的信号。
class Watcher():
def __init__(self):
self.child = os.fork()
if self.child == 0:
return
else:
self.watch()
def watch(self):
try:
os.wait()
except KeyboardInterrupt:
self.kill()
sys.exit()
def kill(self):
try:
os.kill(self.child, signal.SIGKILL)
except OSError:
pass
在开始工作线程之前启动一个观察者,比如
def main():
init()
Watcher()
start_your_thread1()
start_your_thread2()
start_your_thread3()
最终的,你原来的方式,复杂的生产者和消费者方式。
只需删除最后的 join(),并为主线程添加一些任务。
我更喜欢第二个选项,它很容易使用,并解决了 Python 中多线程程序的两个问题,(1) 信号可能被传递到任何线程(这只是一个故障)和(2 ) 如果获取信号的线程正在等待,则忽略该信号(这是一个错误)。
有关 Watcher 的更多详细信息,请参阅本书的附录 A The Little Book of Semaphores
我取自pycamera docs的例子进行快速捕获和处理,并添加了一个sigint事件处理程序来捕获键盘中断:
import io
import time
import threading
import picamera
# Create a pool of image processors
done = False
lock = threading.Lock()
pool = []
def signal_handler(signal, frame):
global done
print 'You pressed Ctrl+C!'
done=True
sys.exit()
signal.signal(signal.SIGINT, signal_handler)
class ImageProcessor(threading.Thread):
def __init__(self):
super(ImageProcessor, self).__init__()
self.stream = io.BytesIO()
self.event = threading.Event()
self.terminated = False
self.daemon=True;
self.start()
def run(self):
# This method runs in a separate thread
global done
while not self.terminated:
# Wait for an image to be written to the stream
if self.event.wait(1):
try:
self.stream.seek(0)
# Read the image and do some processing on it
#Image.open(self.stream)
#...
#...
# Set done to True if you want the script to terminate
# at some point
#done=True
finally:
# Reset the stream and event
self.stream.seek(0)
self.stream.truncate()
self.event.clear()
# Return ourselves to the pool
with lock:
pool.append(self)
def streams():
while not done:
with lock:
if pool:
processor = pool.pop()
else:
processor = None
if processor:
yield processor.stream
processor.event.set()
else:
# When the pool is starved, wait a while for it to refill
time.sleep(0.1)
with picamera.PiCamera() as camera:
pool = [ImageProcessor() for i in range(4)]
camera.resolution = (640, 480)
camera.framerate = 30
camera.start_preview()
time.sleep(2)
camera.capture_sequence(streams(), use_video_port=True)
# Shut down the processors in an orderly fashion
while pool:
with lock:
processor = pool.pop()
processor.terminated = True
processor.join()
但从未捕获到中断信号。
在 camera.capture_sequence(streams(), use_video_port=True)
运行之前捕获信号,在 capture_sequence
启动后不会调用信号处理程序。
我是 python 的新手,所以也许答案很简单。我在这里做错了什么?
编辑:
如果我删除以下代码,信号就会被捕获:
yield processor.stream
在您的代码中,done
变量是一个全局变量。
所以,每当你想在函数内部修改它时,你都需要使用关键字global
,否则它就变成了一个局部变量。
您应该像这样修复您的代码:
import signal
import sys
done = False
def signal_handler(signal, frame):
global done
print('You pressed Ctrl+C!')
done = True
sys.exit()
signal.signal(signal.SIGINT, signal_handler)
问题在于您正在使用 thread.join()
,它会阻塞主线程,这意味着您的程序必须等到您加入的那个线程结束才能继续。
信号总是会被主进程捕获,因为它是接收信号的进程,它是具有线程的进程。
关于如何处理主线程和CTRL+C有很多答案,我给你三个选择,
首先,为 join() 调用添加超时:
thread1.join(60)
详情 here
其次,启动一个新的进程来处理杀死程序的信号。
class Watcher():
def __init__(self):
self.child = os.fork()
if self.child == 0:
return
else:
self.watch()
def watch(self):
try:
os.wait()
except KeyboardInterrupt:
self.kill()
sys.exit()
def kill(self):
try:
os.kill(self.child, signal.SIGKILL)
except OSError:
pass
在开始工作线程之前启动一个观察者,比如
def main():
init()
Watcher()
start_your_thread1()
start_your_thread2()
start_your_thread3()
最终的,你原来的方式,复杂的生产者和消费者方式。
只需删除最后的 join(),并为主线程添加一些任务。
我更喜欢第二个选项,它很容易使用,并解决了 Python 中多线程程序的两个问题,(1) 信号可能被传递到任何线程(这只是一个故障)和(2 ) 如果获取信号的线程正在等待,则忽略该信号(这是一个错误)。 有关 Watcher 的更多详细信息,请参阅本书的附录 A The Little Book of Semaphores