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