Python pexpect多线程程序在生成shell时挂起

Python pexpect多线程程序在生成shell时挂起,python,multithreading,fork,pexpect,Python,Multithreading,Fork,Pexpect,我有一个多线程程序。程序运行时,在调用pexpect.spawn()期间,某些线程有时会挂起 该程序在每个线程中创建一个会话对象,会话对象在创建时生成一个pexpect会话。在程序中,每个会话对象都绑定到一个特定线程 class CustomizedThread(threading.Thread): __init__(self, thread_name): super().__init__(name=thread_name) def run(self):

我有一个多线程程序。程序运行时,在调用pexpect.spawn()期间,某些线程有时会挂起

该程序在每个线程中创建一个会话对象,会话对象在创建时生成一个pexpect会话。在程序中,每个会话对象都绑定到一个特定线程

class CustomizedThread(threading.Thread):
    __init__(self, thread_name):
        super().__init__(name=thread_name)

    def run(self):
        session = Session()
        ...

class Session:
    __init__(self, session_name):
        self.name = session_name
        print('Thread {} is spawning a shell in session {}.'.format(
            threading.currentThread(), session.name))
        self.pexpect_session = pexpect.spawn('/bin/sh')
        print('Thread {} finished spawning a shell in session {}.'.format(
            threading.currentThread(), session.name))
        self.pexpect_session.sendline('ssh MACHINE_NAME')
        ...

    __del__(self):
        print('Thread {} is cleaning up session {}.'.format(
            threading.currentThread(), session.name))
        self.pexepect_session.close(force=True)
以下是线程2挂起时的输出示例,在调用线程2上的pexpect.spawn()时会触发会话对象1的析构函数

...
Thread 1 is spawning a shell in session 1.
Thread 1 finished spawning a shell in session 1.
Thread 2 is spawning a shell in session 2.
Thread 2 is cleaning up session 1.
将挂起进程附加到gdb,我得到了以下堆栈跟踪。它显示线程在尝试将异常消息写入文件描述符时挂起:

(gdb) where
#0  0x00007fff9628391a in write () from /usr/lib/system/libsystem_kernel.dylib
#1  0x000000010ed7aa22 in _Py_write_impl (fd=2, buf=0x10f3f1010, count=76, gil_held=1) at ../Python/fileutils.c:1269
#2  0x000000010ed7a9a1 in _Py_write (fd=2, buf=0x10f3f1010, count=76) at ../Python/fileutils.c:1327
#3  0x000000010ede8795 in _io_FileIO_write_impl (self=0x10f3875f8, b=0x7000013dd168) at ../Modules/_io/fileio.c:840
#4  0x000000010ede7957 in _io_FileIO_write (self=0x10f3875f8, arg=0x11312c148)
at ../Modules/_io/clinic/fileio.c.h:245
#5  0x000000010ebbfd72 in PyCFunction_Call (func=0x112ed7b98, args=0x112fc6330, kwds=0x0)
at ../Objects/methodobject.c:134
#6  0x000000010eb2803d in PyObject_Call (func=0x112ed7b98, arg=0x112fc6330, kw=0x0) at ../Objects/abstract.c:2165
#7  0x000000010eb290de in PyObject_CallMethodObjArgs (callable=0x112ed7b98, name=0x10f234d40)
at ../Objects/abstract.c:2394
#8  0x000000010edf0456 in _bufferedwriter_raw_write (self=0x10f25de58,
start=0x10f3f1010 "\nThread 2 is cleaning up session 1. \"terminated\" is 0, but there was no child process. Did someone else call waitpid() on our process?\n"...,
len=76) at ../Modules/_io/bufferedio.c:1847
异常消息“已终止”为0,但没有子进程。是否有其他人在我们的进程上调用了waitpid()?是从pexpect会话关闭的行中发出的

self.pexepect_session.close(force=True)
另外,在pexpect的spawn()方法中,进程被分叉(我在gdb中附加到的进程)以执行“/bin/sh”,并创建一个管道以将任何异常消息写入父进程


看起来分叉进程垃圾收集了另一个线程的会话对象,但在尝试关闭另一个线程上的会话时捕获了异常。进程正在挂起将异常消息写入管道,因为异常消息应该是从另一端读取的。

与其使用线程生成Shell和pexpect运行ssh,不如使用。这是一个非常好的、成熟的库,它实现ssh而不必向外掏壳。您可以在一个线程中运行多个会话,这在Python中可能会更高效——请参见此处:

切换到多处理可以解决这个问题。混合使用多线程和fork是有问题的。

谢谢!这个程序需要的不仅仅是SSH。它还用于远程连接到一些嵌入式板,Paramiko不支持这种功能。@Tsu Hao:那怎么样?