为什么stdin会阻止整个事件循环而不写入它?

时间:2016-12-19 13:22:58

标签: python libuv

最近,我正在为一个需要监控多个子进程的项目工作。 stdout同时写stdin。为了实现它,我使用pyuv作为后台线程上运行的事件循环,并动态添加子进程的句柄。以下是模拟上述过程的python代码:

import pyuv, threading, time
import sys, os
loop = pyuv.Loop()
class Process:
    def __init__(self, loop):
        self.in_pipe  = pyuv.Pipe(loop)
        self.out_pipe = pyuv.Pipe(loop)
        self.err_pipe = pyuv.Pipe(loop)
        self.loop = loop

        pass

    def on_read(self, pipe_handle, data, error):
        print("data --> %s" % data)
        pass

    def write(self, data):
        self.in_pipe.write(data)
        pass

    def on_finish(proc, handle, status, signal):
        print("finish_cb", status, signal)

    def start(self):
        stdin  = pyuv.StdIO(stream=self.in_pipe, flags=pyuv.UV_CREATE_PIPE | pyuv.UV_READABLE_PIPE)
        #stdout = pyuv.StdIO(stream=self.out_pipe, flags=pyuv.UV_CREATE_PIPE | pyuv.UV_WRITABLE_PIPE)
        stdout = pyuv.StdIO(stream=self.out_pipe, flags=pyuv.UV_CREATE_PIPE | pyuv.UV_WRITABLE_PIPE)
        stderr = pyuv.StdIO(stream=self.err_pipe, flags=pyuv.UV_CREATE_PIPE | pyuv.UV_WRITABLE_PIPE)
        s = pyuv.Process.spawn(self.loop, args=["python", "-i"], exit_callback=self.on_finish, stdio=[stdin, stdout,stderr])
        self.out_pipe.start_read(self.on_read)
        self.err_pipe.start_read(self.on_read)

_index = 0

proc_list = []

# add process first
# or the loop will just skip
p = Process(loop)
p.start()
proc_list.append(p)

def _run():
    loop.run()
    print("loop fin")

t = threading.Thread(target=_run)
t.setDaemon(True)
t.start()

while _index < 4:
    _index += 1
    # adding the process handle into the loop
    time.sleep(1)
    p = Process(loop)
    p.start()
    proc_list.append(p)

 #   for i in proc_list:
 #      i.write(b"2+2\n")

我的预期是在添加新进程后,它会立即打印其标准输出。因此,我将看到Python的4次REPL消息。

然而,当我执行它时,它很糟糕,就像这样:

(env) ➜  _draft git:(pyuv-watcher) ✗ python libuv.py
data --> b'Python 3.5.2 (default, Jun 28 2016, 08:46:01) \n[GCC 6.1.1 20160602] on linux\nType "help", "copyright", "credits" or "license" for more information.\n'
data --> b'>>> '
[nothing then, until 4 seconds later.]
(env) ➜  _draft git:(pyuv-watcher) ✗

但是,当我取消注释最后2行时,即将数据写入进程的stdin。它有所不同:

(env) ➜  _draft git:(pyuv-watcher) ✗ python libuv.py
data --> b'Python 3.5.2 (default, Jun 28 2016, 08:46:01) \n[GCC 6.1.1 20160602] on linux\nType "help", "copyright", "credits" or "license" for more information.\n'
data --> b'>>> '
data --> b'4\n'
data --> b'>>> '
data --> b'Python 3.5.2 (default, Jun 28 2016, 08:46:01) \n[GCC 6.1.1 20160602] on linux\nType "help", "copyright", "credits" or "license" for more information.\n'
data --> b'>>> '
data --> b'4\n'
data --> b'>>> '
data --> b'4\n'
data --> b'>>> '
data --> b'4\n'
data --> b'>>> '
data --> b'Python 3.5.2 (default, Jun 28 2016, 08:46:01) \n[GCC 6.1.1 20160602] on linux\nType "help", "copyright", "credits" or "license" for more information.\n'
data --> b'>>> '
data --> b'>>> '
data --> b'4\n'
data --> b'4\n'
data --> b'>>> '
data --> b'4\n'
data --> b'>>> '
data --> b'4\n'
data --> b'>>> '
data --> b'Python 3.5.2 (default, Jun 28 2016, 08:46:01) \n[GCC 6.1.1 20160602] on linux\nType "help", "copyright", "credits" or "license" for more information.\n'
data --> b'>>> '
data --> b'4\n'
data --> b'>>> '
(env) ➜  _draft git:(pyuv-watcher) ✗ 

似乎如果只添加一个Process句柄而不将数据写入stdin,stdin的等待过程将阻止整个过程。怎么会这样?以及如何通过一个事件循环同时与不同进程交互?

1 个答案:

答案 0 :(得分:0)

您最有可能被未定义的行为击中。 libuv(以及扩展名,pyuv)不是线程安全的。这意味着您不能在后台线程中运行循环并开始从另一个线程向其添加句柄。