为什么管道的变量在python中不能作为线程间通信的管道

为什么管道的变量在python中不能作为线程间通信的管道,python,pipe,Python,Pipe,在我探索IPC的过程中,特别是在python 3.4.1中处理线程和套接字, 我经历了一些有点奇怪的事情,我不太明白发生了什么。 目前,我正在使用一个匿名管道和os.pipe进行发送 连接到保持套接字连接的线程的终止信号 我的目标是以优雅的方式终止线程。起初我尝试使用布尔标志,但由于select调用被阻塞,我不得不向select.select可以读取的对象发送终止信号;一个套接字、管道、stdin等,因此中断了select调用 在我发现如何使用管道与线程通信并穿透select调用之前,我中断了对

在我探索IPC的过程中,特别是在python 3.4.1中处理线程和套接字, 我经历了一些有点奇怪的事情,我不太明白发生了什么。 目前,我正在使用一个匿名管道和os.pipe进行发送 连接到保持套接字连接的线程的终止信号

我的目标是以优雅的方式终止线程。起初我尝试使用布尔标志,但由于select调用被阻塞,我不得不向select.select可以读取的对象发送终止信号;一个套接字、管道、stdin等,因此中断了select调用

在我发现如何使用管道与线程通信并穿透select调用之前,我中断了对测试分支的开发

让我解释一下我的情况。 基本上这是有效的…:

import os
import threading
import select

class MyThread(threading.Thread):
    def __init__(self, pipein):
        threading.Thread.__init__(self)
        # The pipe to listen for terminate signals on
        self.pipein = pipein
        self.stopped = False
        self.inputs = [self.pipein]

    def run(self):
        print("Thread-1 Started")
        while not self.stopped:
            inputready, outputready, errors = select.select(self.inputs, [], [])
            for i in inputready:
                if i == self.pipein:
                    signal = os.read(self.pipein, 64)
                    # 64 An arbitrary length that should be enough in any case
                    print("The thread received stuff on the pipe: %s" % signal)
                    if signal == b'stop':
                        print("Stop command received.")
                        print("Exiting.")
                        self.stopped = True
                        break

if __name__ == "__main__":

    # Create the communication pipes
    pipe = os.pipe()

    # Start the worker thread
    print("Starting the worker thread...")
    t = MyThread(pipe[0])
    t.start()
    print("Worker thread started.")


    stopped = False

    # Enter the main loop
    while not stopped:
        command = input("Command to send to thread: ")
        os.write(pipe[1], bytes(command, 'UTF-8'))
        stopped = True
如果我在终端中键入“停止”,我会得到:

localhost:ipc.git $ python3 pipes.py
Starting the worker thread...
Thread-1 Started
Worker thread started.
Command to send to thread: stop
The thread received stuff on the pipe: b'stop'
Stop command received.
Exiting.
localhost:ipc.git $ clear
但这并不是:

import os
import threading
import select

class MyThread(threading.Thread):
    def __init__(self, pipein):
        threading.Thread.__init__(self)
        # The pipe to listen for terminate signals on
        self.pipein = pipein
        self.stopped = False
        self.inputs = [self.pipein]

    def run(self):
        print("Thread-1 Started")
        while not self.stopped:
            inputready, outputready, errors = select.select(self.inputs, [], [])
            for i in inputready:
                if i == self.pipein:
                    signal = os.read(self.pipein, 64)
                    # 64 An arbitrary length that should be enough in any case
                    print("The thread received stuff on the pipe: %s" % signal)
                    if signal == b'stop':
                        print("Stop command received.")
                        print("Exiting.")
                        self.stopped = True
                        break

if __name__ == "__main__":

    # Create the communication pipes
    pipein, pipeout = os.pipe() # Seperate into reader fd and writer fd

    # Start the worker thread
    print("Starting the worker thread...")
    t = MyThread(pipein) # Give the thread the receiver
    t.start()
    print("Worker thread started.")


    stopped = False

    # Enter the main loop
    while not stopped:
        command = input("Command to send to thread: ")
        # Write on the variable of pipe[1]: pipeout
        os.write(pipeout, bytes(command, 'UTF-8'))
        stopped = True
不同的是,得到一个

OSError: [Errno 9] Bad file descriptor
尝试从管道创建的变量读取或写入时

比如:

但是,如果我使用管道[0]和管道[1]分别对os.read和os.write进行读取和写入,它就可以正常工作了

因此,为管道[0]或管道[1]创建任何类型的变量都不起作用,因此我得到了和OSError。 如果我创建一个类调用Communicator并将管道[0]和管道[1]作为实例变量,同样的情况也适用

谁能解释一下为什么会这样?我将永远无法写入管道[1]的变量,或者这仅仅是因为我在线程之间运行

如果您知道线程间通信的另一种方式,可以在select调用中使用或中断select调用,我洗耳恭听

我尝试了io.StringIO或io的一个实例。{OtherIOHere},但它们不支持fileno调用,因此无法使用select

我想创建一个类来包含我的通信管道,以提高可用性,但在我找到管道变量不起作用的原因之前,我不能

如有任何意见或建议,我们将不胜感激

编辑: 添加了一些调试测试:

import os
import threading
import time
import select

class MyThread(threading.Thread):
    def __init__(self, pipein):
        threading.Thread.__init__(self)
        self.pipein = pipein
        self.stopped = False
        self.inputs = [self.pipein]

    def run(self):
        print("Thread-1 Started")
        while not self.stopped:
            inputready, outputready, errors = select.select(self.inputs, [], [])
            for i in inputready:
                if i == self.pipein:
                    signal = os.read(self.pipein, 64)
                    print("The thread received stuff on the pipe: %s" % signal)
                    if signal == b'stop':
                        print("Stop command received.")
                        print("Exiting.")
                        self.stopped = True
                        break

if __name__ == "__main__":

    # Create the communication pipes
    pipe = os.pipe()
    pipein = pipe[0]
    pipeout = pipe[1]

    # Some Print debugs
    print(type(pipein))
    print(type(pipeout))
    print(pipein)
    print(pipeout)
    print(type(pipe))
    print(type(pipe[0]))
    print(type(pipe[1]))
    print(pipe[0])
    print(pipe[1])


    # Start the worker thread
    print("Starting the worker thread...")
    t = MyThread(pipein)
    t.start()
    print("Worker thread started.")

    # Enter the main loop

    stopped = False


    while not stopped:
        command = input("Command to send to thread: ")
        os.write(pipeout, bytes(command, 'UTF-8'))
        stopped = True
@戴夫,有趣的是,这个现在起作用了,我一点也不知道为什么。我也这么做了,这是两个不同的项目。在这两种情况下,我都无法写入管道[1]的变量

 localhost:ipc.git $ python3 pipes.py
<class 'int'>
<class 'int'>
3
4
<class 'tuple'>
<class 'int'>
<class 'int'>
3
4
Starting the worker thread...
Thread-1 Started
Worker thread started.
Command to send to thread: stop
The thread received stuff on the pipe: b'stop'
Stop command received.
Exiting.
localhost:ipc.git $ 

谢谢你们的帮助。

我把你们的两个代码示例复制并粘贴到我的macbook上的两个文件中,用macports上的python 3.4.1运行它们,输入“停止”,它们都工作了

您使用的是什么操作系统


编辑:看起来你已经修好了。干得好

除非我遗漏了什么,否则这肯定有用。你是说你唯一改变的是管道变量从元组的括号访问变为单独的变量?这两个值应该只是整数。尝试在中添加一些print语句以进行调试,以查看描述符是什么。你是否在两个版本之间以完全相同的方式运行东西,从完全相同的目录,等等?我很欣赏你的分析调试技能dave,我完全按照你在发布问题之前的建议做了。也许我应该包括这些测试。是的,它们只是整数,相同的目录,没有任何更改,我是一个至少要检查3次的人。我现在将编辑这篇文章,并包括打印测试的结果。戴夫,它似乎正在工作。使用pipeout=pipe[1]和pipein,pipeout=os.pipe,我不知道会发生什么变化。可能是一些底层管道pet PEEV或权限。让我试着创建一个Communicator类来保存管道,看看它是否有效。是的,这是最疯狂的事情。我想:这没有道理。但有时这些事情会发生。我使用MacOSX 10.9.2和Python 3.4.1。在communicator类中测试管道之后,我将在将来的浏览器中更新我的问题,因为这是我第一次遇到问题的时候。
import os
import threading
import time
import select

class MyThread(threading.Thread):
    def __init__(self, pipein):
        threading.Thread.__init__(self)
        self.pipein = pipein
        self.stopped = False
        self.inputs = [self.pipein]

    def run(self):
        print("Thread-1 Started")
        while not self.stopped:
            inputready, outputready, errors = select.select(self.inputs, [], [])
            for i in inputready:
                if i == self.pipein:
                    signal = os.read(self.pipein, 64)
                    print("The thread received stuff on the pipe: %s" % signal)
                    if signal == b'stop':
                        print("Stop command received.")
                        print("Exiting.")
                        self.stopped = True
                        break

if __name__ == "__main__":

    # Create the communication pipes
    pipe = os.pipe()
    pipein = pipe[0]
    pipeout = pipe[1]

    # Some Print debugs
    print(type(pipein))
    print(type(pipeout))
    print(pipein)
    print(pipeout)
    print(type(pipe))
    print(type(pipe[0]))
    print(type(pipe[1]))
    print(pipe[0])
    print(pipe[1])


    # Start the worker thread
    print("Starting the worker thread...")
    t = MyThread(pipein)
    t.start()
    print("Worker thread started.")

    # Enter the main loop

    stopped = False


    while not stopped:
        command = input("Command to send to thread: ")
        os.write(pipeout, bytes(command, 'UTF-8'))
        stopped = True
 localhost:ipc.git $ python3 pipes.py
<class 'int'>
<class 'int'>
3
4
<class 'tuple'>
<class 'int'>
<class 'int'>
3
4
Starting the worker thread...
Thread-1 Started
Worker thread started.
Command to send to thread: stop
The thread received stuff on the pipe: b'stop'
Stop command received.
Exiting.
localhost:ipc.git $ 
class MyThread(threading.Thread):
    def __init__(self, comm):
        threading.Thread.__init__(self)
        self.comm = comm
        self.stopped = False
        self.inputs = [self.comm.pipein]

    def run(self):
        print("Thread-1 Started")
        while not self.stopped:
            inputready, outputready, errors = select.select(self.inputs, [], [])
            for i in inputready:
                if i == self.comm.pipein:
                    signal = self.comm.read()
                    print("The thread received stuff on the pipe: %s" % signal)
                    if signal == b'stop':
                        print("Stop command received.")
                        print("Exiting.")
                        self.stopped = True
                        break

class Communicator:
    def __init__(self):
        self.pipe = os.pipe()
        self.pipein = self.pipe[0]
        self.pipeout = self.pipe[1]

    def write(self, msg):
        os.write(self.pipeout, msg)

    def read(self):
        return os.read(self.pipein, 64)

if __name__ == "__main__":

    # Create the communication pipes
    #pipe = os.pipe()
    #pipein = pipe[0]
    #pipeout = pipe[1]

    # Use the communicator class
    comm = Communicator()


    # Some Print debugs


    # Start the worker thread
    print("Starting the worker thread...")
    t = MyThread(comm)
    t.start()
    print("Worker thread started.")

    # Enter the main loop

    stopped = False

    while not stopped:
        command = input("Command to send to thread: ")
        comm.write(b'stop')
        stopped = True