input() 正在阻止进程的使用

问题描述

我遇到了多处理问题.如果我在线程中等待输入,则进程不会启动.

I have a problem with multiprocessing. If I am waiting for input in a thread the process is not starting.

将输入放入后台队列的类:

The class to put the input into a queue in the background:

class InputCatcher(Thread):
    def __init__(self, input_queue):
        Thread.__init__(self)
        self.input_queue = input_queue

    def run(self):
        while True:
            self.input_queue.put(input()) # <<-- Without this it works!

不会开始的类:

class Usb(Process):
    def __init__(self, port, ctrl=Controller()):
        Process.__init__(self)
        self.usb_port = port
        self.ctrl = ctrl


    def run(self):
        self.ctrl.usb_ports.append(self.usb_port)
        ser = Serial(self.usb_port, 115200)
        while True:
            dsl = ser.readline()
            self.ctrl.get_dataset_queue().put(['USBDS', dsl])
            print(dsl)

开始于:

ic = InputCatcher(self.input_queue)
ic.setDaemon(True)
ic.start()

usbs = []
for port in usb_ports():
    if not port in ctrl.xbee_ports:
        usbs.append(Usb(port, ctrl))

for usb in usbs:
    usb.daemon = True
    usb.start()


解决方案

当你调用 input 时,它阻塞了整个 Python 进程,而不仅仅是它运行的线程.发生这种情况是因为从与从任何其他类似文件的对象中读取一样,STDIN 涉及进行阻塞系统调用 - 即 input 阻塞以等待用户输入发生在操作系统级别,而不是在 Python 自己的线程管理代码内部.Python 线程对 OS 进程调度程序本质上是不可见的,因此 Python 本身 会被阻塞.

When you call input, it is blocking the entire Python process, not just the thread it runs in. This happens because reading from STDIN, like reading from any other file-like object, involves making a blocking syscall - that is, input blocking to wait for user input happens at the OS level, rather than inside Python's own thread management code. Python threads are essentially invisible to the OS process scheduler, so Python itself gets blocked.

解决此类阻塞问题的常用方法是使用进程而不是线程.如果你把 InputCatcher 变成一个进程而不是线程,那么它就变成了一个独立的操作系统级进程,操作系统可以独立调度,因此系统调用只会阻塞 那个 进程而不是主进程.

The usual way around blocking problems like this is to use processes instead of threads. If you make InputCatcher into a process rather than a thread, then it becomes a separate OS-level process that the OS can schedule independently, and so the syscall will only block that process and not the main one.

除了,Python 会自动在您生成进程时关闭 STDIN.

Except, that Python automatically closes STDIN when you spawn a process.

因此,您需要在主进程中拥有队列的生产者,而在另一个进程中只有消费者.这也是一个微不足道的适应 - 不要启动生产者 (InputCatcher) 直到在所有消费者进程已经产生之后运行.这涉及到移动线路:

So, you would need to have the producer for the queue in the main process, and only the consumer in another one. This is also a trivial adaption - don't start the producer (InputCatcher) running until after all of the consumer processes have spawned. That involves moving the line:

ic.start()

到下面两个循环.但在这种情况下,根本不需要将其作为背景 - 它不会与其他事物同时运行.因此,您可以完全忘记 InputCatcher 类,只需像这样编写代码:

to below the two loops. But in this case, there's no need for that to be backgrounded at all - it doesn't run simultaneously with other things. So, you can forget about the InputCatcher class entirely, and just write your code like this:

for usb in usbs:
    usb.daemon = True
    usb.start()

while True:
    input_queue.put(input())

您可能还想考虑一个特定的输入——比如空字符串——来结束程序.在主运行中输入,只需结束循环就可以轻松完成:

You might also want to consider a particular input - say, the empty string - to end the program. Having the input in the main run makes this really easy by just ending the loop:

while True:
    data = input('Type data; or enter to exit: ')
    if not data:
        break
    input_queue.put(data)

相关文章