Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/358.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python:从线程化子进程的stdout进行非阻塞读取_Python_Multithreading_Subprocess_Nonblocking - Fatal编程技术网

Python:从线程化子进程的stdout进行非阻塞读取

Python:从线程化子进程的stdout进行非阻塞读取,python,multithreading,subprocess,nonblocking,Python,Multithreading,Subprocess,Nonblocking,我有一个脚本(worker.py),它以 1 2 3 . . . n 其中n是此脚本中循环将进行的某个恒定迭代次数。在另一个脚本(service_controller.py)中,我启动了许多线程,每个线程使用subprocess.Popen(stdout=subprocess.PIPE,…)启动一个子流程;现在,在我的主线程(service_controller.py)中,我想读取每个线程的worker.py子进程的输出,并使用它来计算完成之前剩余时间的估计值 我拥有从worker.py读取标

我有一个脚本(worker.py),它以

1
2
3
.
.
.
n
其中n是此脚本中循环将进行的某个恒定迭代次数。在另一个脚本(service_controller.py)中,我启动了许多线程,每个线程使用subprocess.Popen(stdout=subprocess.PIPE,…)启动一个子流程;现在,在我的主线程(service_controller.py)中,我想读取每个线程的worker.py子进程的输出,并使用它来计算完成之前剩余时间的估计值

我拥有从worker.py读取标准输出并确定最后打印的数字的所有逻辑。问题是,我不知道如何以非阻塞的方式实现这一点。如果我读取一个常量bufsize,那么每次读取都将等待来自每个worker的相同数据。我尝试了很多方法,包括使用fcntl、选择+os.read等。我在这里的最佳选择是什么?如果需要的话,我可以发布我的源代码,但我认为解释足够好地描述了这个问题

谢谢你的帮助

编辑
添加示例代码

我有一个启动子流程的工人

class WorkerThread(threading.Thread):
    def __init__(self):
        self.completed = 0
        self.process = None
        self.lock = threading.RLock()
        threading.Thread.__init__(self)

    def run(self):
        cmd = ["/path/to/script", "arg1", "arg2"]
        self.process = subprocess.Popen(cmd, stdout=subprocess.PIPE, bufsize=1, shell=False)
        #flags = fcntl.fcntl(self.process.stdout, fcntl.F_GETFL)
        #fcntl.fcntl(self.process.stdout.fileno(), fcntl.F_SETFL, flags | os.O_NONBLOCK)

    def get_completed(self):
        self.lock.acquire();
        fd = select.select([self.process.stdout.fileno()], [], [], 5)[0]
        if fd:
            self.data += os.read(fd, 1)
            try:
                self.completed = int(self.data.split("\n")[-2])
            except IndexError:
                pass
        self.lock.release()
        return self.completed
然后我有一个线程管理器

class ThreadManager():
    def __init__(self):
        self.pool = []
        self.running = []
        self.lock = threading.Lock()

    def clean_pool(self, pool):
        for worker in [x for x in pool is not x.isAlive()]:
            worker.join()
            pool.remove(worker)
            del worker
        return pool

    def run(self, concurrent=5):
        while len(self.running) + len(self.pool) > 0:
            self.clean_pool(self.running)
            n = min(max(concurrent - len(self.running), 0), len(self.pool))
            if n > 0:
                for worker in self.pool[0:n]:
                    worker.start()
                self.running.extend(self.pool[0:n])
                del self.pool[0:n]
            time.sleep(.01)
         for worker in self.running + self.pool:
             worker.join()
还有一些代码来运行它

threadManager = ThreadManager()
for i in xrange(0, 5):
    threadManager.pool.append(WorkerThread())
threadManager.run()

我已经删除了其他代码的日志,希望能够找到问题所在。

与其让您的服务控制器被I/o访问阻塞,不如只让线程循环读取自己控制的进程输出

然后,您可以在线程对象中使用方法控制进程,以获取最后一次轮询的输出


当然,在这种情况下,不要忘记使用一些锁定机制来保护缓冲区,该缓冲区将被线程用来填充缓冲区,也被控制器调用以获取缓冲区。

与其让服务控制器被i/o访问阻塞,不如只让线程循环读取自己控制的进程输出

然后,您可以在线程对象中使用方法控制进程,以获取最后一次轮询的输出


当然,在这种情况下,不要忘记使用某种锁定机制来保护缓冲区,该缓冲区将被线程用来填充缓冲区,也被控制器调用以获取缓冲区。

您的方法WorkerThread.run()启动子进程,然后立即终止。Run()需要执行轮询并更新WorkerThread.completed,直到子进程完成。

您的方法WorkerThread.Run()启动子进程,然后立即终止。Run()需要执行轮询并更新WorkerThread.completed,直到子进程完成。

您是在Linux还是其他Unix上?如果是这样的话,select+os.read 1 byte应该可以正常工作——您能告诉我们您的代码以及它给您带来的错误或不当行为吗?这实际上是在windoze上运行的,用于开发的将在Fedora或用于生产的os X上运行。您是在Linux还是其他Unix上?如果是这样的话,select+os.read 1 byte应该可以很好地工作——你能告诉我们你的代码以及它给你带来了什么错误或不当行为吗?这实际上是在windoze上运行的开发将在Fedora或os X上进行生产。我离你的建议有多远?我有线程对象控制进程以获取最后轮询的输出…您的get_completed方法只填充self.completed,我建议将其重命名为update_completed。然后添加一个get_completed方法,返回self.completed(添加threading.RLock以保护对它的访问)。然后在线程管理器中,您可以定期对工作线程调用get_completed。我已经在阅读中添加了RLock,但我仍然有同样的问题。我只是不能让我的头围绕着这个。也许今晚睡一觉会让我获得我所期待的突破。我离你的建议还远吗?我有线程对象控制进程以获取最后轮询的输出…您的get_completed方法只填充self.completed,我建议将其重命名为update_completed。然后添加一个get_completed方法,返回self.completed(添加threading.RLock以保护对它的访问)。然后在线程管理器中,您可以定期对工作线程调用get_completed。我已经在阅读中添加了RLock,但我仍然有同样的问题。我只是不能让我的头围绕着这个。也许今晚睡一觉会让我有我所期待的突破。