简体   繁体   English

subprocess.Popen() 卡住了

[英]subprocess.Popen() getting stuck

My question我的问题

I encountered a hang-up issue with the combination of threading, multiprocessing, and subprocess.我遇到了线程、多处理和子进程组合的挂断问题。 I simplified my situation as below.我简化了我的情况如下。

import subprocess
import threading
import multiprocessing

class dummy_proc(multiprocessing.Process):
    def run(self):
        print('run')
        while True:
            pass

class popen_thread(threading.Thread):
    def run(self):
        proc = subprocess.Popen('ls -la'.split(), shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout_byte, stderr_byte = proc.communicate()
        rc = proc.returncode
        print(rc)

if __name__ == '__main__':
    print('start')
    t = popen_thread()
    t.start()

    p = dummy_proc()
    p.start()
    t.join()
    p.terminate()

In this script, a thread and a process are generated, respectively.在这个脚本中,分别生成了一个线程和一个进程。 The thread just issues the system command ls -la .该线程仅发出系统命令ls -la The process just loops infinitely.这个过程只是无限循环。 When the thread finishes getting the return code of the system command, it terminates the process and exits immediately.当线程获取完系统命令的返回码后,终止进程,立即退出。

When I run this script again and again, it sometimes hangs up.当我一次又一次地运行这个脚本时,它有时会挂断。 I googled this situation and found some articles which seem to be related.我用谷歌搜索了这种情况,发现了一些似乎相关的文章。

So, I guess the hang-up issue is explained something like below.所以,我想挂断问题的解释如下。

  1. The process is generated between Popen() and communicate() .该过程在Popen()communicate()之间生成。
  2. It inherits some "blocking" status of the thread, and it is never released.它继承了线程的某些“阻塞”状态,并且永远不会被释放。
  3. It prevents the thread from acquiring the result of the communitare() .它阻止线程获取communitare()的结果。

But I'm not 100% confident, so it would be great if someone helped me explain what happens here.但我不是 100% 有信心,所以如果有人帮我解释这里发生的事情,那就太好了。

My environment我的环境

I used following environment.我使用了以下环境。

$ uname -a
Linux dell-vostro5490 5.10.96-1-MANJARO #1 SMP PREEMPT Tue Feb 1 16:57:46 UTC 2022 x86_64 GNU/Linux
$ python3 --version
Python 3.9.2

I also tried following environment and got the same result.我也尝试了以下环境并得到了相同的结果。

$ uname -a
Linux raspberrypi 5.10.17+ #2 Tue Jul 6 21:58:58 PDT 2021 armv6l GNU/Linux
$ python3 --version
Python 3.7.3

What I tried我试过的

  • Use spawn instead of fork for multiprocessing.使用 spawn 而不是 fork 进行多处理。
  • Use thread instead of process for dummy_proc .对于dummy_proc使用线程而不是进程。

In both cases, the issue disappeared.在这两种情况下,问题都消失了。 So, I guess this issue is related with the behavior of the fork...所以,我想这个问题与分叉的行为有关......

This is a bit too long for a comment and so...这对于评论来说有点太长了,所以......

I am having a problem understanding your statement that the problem disappears when you "Use thread instead of process for dummy_proc."我无法理解您关于当您“为 dummy_proc 使用线程而不是进程”时问题消失的说法。

The hanging problem as I understand it is "that fork() only copies the calling thread, and any mutexes held in child threads will be forever locked in the forked child."据我所知,悬而未决的问题是“fork() 只复制调用线程,子线程中持有的任何互斥锁将永远锁定在分叉的子线程中。” In other words, the hanging problem arises when a fork is done when there exists one or more threads other than the main thread (ie, the one associated with the main process).换句话说,当存在除主线程之外的一个或多个线程(即与主进程相关联的线程)时进行fork时,就会出现挂起问题。

If you execute a subprocess.Popen call from a newly created subprocess or a newly created thread, either way there will be by definition a new thread in existence prior to the fork done to implement the Popen call and I would think the potential for hanging exists.如果您从新创建的子进程或新创建的线程执行subprocess.Popen调用,根据定义,在执行Popen调用的 fork 之前存在一个新线程,我认为存在挂起的可能性.

import subprocess
import threading
import multiprocessing
import os


class popen_process(multiprocessing.Process):
    def run(self):
        print(f'popen_process, PID = {os.getpid()}, TID={threading.current_thread().ident}')
        proc = subprocess.Popen('ls -la'.split(), shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout_byte, stderr_byte = proc.communicate()
        rc = proc.returncode

if __name__ == '__main__':
    print(f'main process, PID = {os.getpid()}, TID={threading.current_thread().ident}')
    multiprocessing.set_start_method('spawn')
    p = popen_process()
    p.start()
    p.join()

Prints:印刷:

main process, PID = 14, TID=140301923051328
popen_process, PID = 16, TID=140246240732992

Note the new thread with TID=140246240732992注意 TID=140246240732992 的新线程

It seems to me that you need to use startup method spawn as long as you are doing the Popen call from another thread or process if you want to be sure of not hanging.在我看来,如果您想确保不挂起,只要您从另一个线程或进程进行Popen调用,就需要使用启动方法 spawn 。 For what it's worth, on my Windows Subsystem for Linux I could not get it to hang with fork using your code after quite a few tries.对于它的价值,在我的 Linux 的 Windows 子系统上,经过多次尝试后,我无法使用您的代码将其挂起。 So I am just going by what the linked answer warns against.所以我只是按照链接的答案警告的内容进行操作。

In any event, in your example code, there seems to be a potential race condition.无论如何,在您的示例代码中,似乎存在潜在的竞争条件。 Let's assume that even though your popen_process is a new thread, its properties are such that it does not give rise to the hanging problem (no mutexes are being held).让我们假设即使您的popen_process是一个新线程,它的属性也不会引起挂起问题(没有互斥量被保留)。 Then the problem would be arising from the creation of the dummy_proc process/thread.然后问题将由dummy_proc进程/线程的创建引起。 The question then becomes whether your call to t1.start() completes the starting of the new process that ultimately runs the ls -la command prior to or after the completion of the creation of the dummy_proc process/thread.那么问题就变成了您对t1.start()的调用是否完成了新进程的启动,该进程最终在dummy_proc进程/线程的创建之前或之后运行ls -la命令。 This timing will determine whether the new dummy_proc thread (there will be one regardless of whether dummy_proc inherits from Process or Thread as we have seen) will exist prior to the creation of the ls -la process.这个时间将决定新的dummy_proc线程(无论dummy_proc是继承自Process还是Thread ,如我们所见,都会有一个线程)在创建ls -la进程之前是否存在。 This race condition might explain why you sometimes were hanging.这种竞争条件可以解释为什么你有时会挂起。 I would have no explanation for why if you make dummy_proc inherit from threading.Thread that you never hang.如果你让dummy_procthreading.Thread继承你永远不会挂起,我将无法解释为什么。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM