管道

    先画一幅图帮助大家理解下管道的基本原理

      

python 两个文件互相引用 python文件之间如何互相通信_python 两个文件互相引用

      现有2个进程A和B,他们都在内存中开辟了空间,那么我们在内存中再开辟一个空间C,作用是连接这两个进程的。对于进程来说内存空间是可以共享的(任何一个进程都可以使用内存,内存当中的空间是用地址来标记的,我们通过查找某一个地址就能找到这个内存)A进程可以不断的向C空间输送东西,B进程可以不断的从C空间读取东西,这就是进程间的通信   

      这个通信方式的原理类似于用文件通信,一个进程往文件里写,另外一个进程从文件里读取,最大的不同在于管道里C的空间是开辟在内存当中,第一是他不容易被截获,第二是进程结束后内存空间他会自动的释放掉,文件就不能自动释放掉。第三就是内存的传输是不需要和磁盘交互,所以相比文件管道的传输效率要高很多,这就是管道的基本原理。

      管道在信息传输上是以流的方式传输, 也就是你从A进程不断的写入,B进程源源不断的读出,A进程先写入的就会被B进程先读出,后写进来的就会被后读出,管道不仅仅可以让A进程写C内存空间收,也可以B进程写,C内存空间收,也就是双向通信,单项通信叫做半双工,双向叫做全双工

2.管道本质

     在内存中开辟一个新的空间,对多个进程可见,在通信形式上形成一种约束

3.创建管道

     multiprocessing------------->Pipe

     1.首先理解Pipe是什么,有人理解为类,那到底是什么?

        >>> from multiprocessing import Pipe
        >>> type(Pipe)

                <class 'method'>

       可以看到是个方法,也就是函数,是通过函数的返回值来完成工作的,不是类

      2.下下来看具体事例:

import os,time
  from multiprocessing import Pipe,Process

  #进程函数
  def func(name):
      time.sleep(1)
      print('父进程的id为:',os.getppid(),"--------",'子进程的id为:',os.getpid())


  #创建五个进程
  if __name__ == '__main__':
      job = []
      for i in range(5):
          p = Process(target=func,args=(i,))
          #把新的进程添加到列表里
          job.append(p)
          p.start()
      for i in job:
          i.join()

          打印结果为:

                父进程的id为: 18416 -------- 子进程的id为: 16152
                父进程的id为: 18416 -------- 子进程的id为: 17436
                父进程的id为: 18416 -------- 子进程的id为: 1832
                父进程的id为: 18416 -------- 子进程的id为: 10232

                父进程的id为: 18416 -------- 子进程的id为: 13716

          结果是过了一秒同时打印这五句话,因为5个进程各自执行,都是一秒后执行

          接下来演示管道是如何操作的

          Pipe(duplex)

              功能:创建一个管道

              参数:duplex默认值为True,表示管道为双向管道(全双工)如果设置为False则为单项管道(半双工)

              返回值:返回两个管道流对象,两个管道流对象分别表示管道的两端,如果参数为True的时候,两个对象均可发送接收,如果为False时,则第一个对象只能接收,第二个就只能发送

           情况一:在上面代码的基础上修改,参数为True的情况

                        

import os,time
    from multiprocessing import Pipe,Process

    #进程函数()
    #a为形参,代表着child_conn
    def func(name,a):
        time.sleep(1)
        #把字符串往管道里发送
        a.send('博主最帅'+str(name))
        print('父进程的id为:',os.getppid(),"--------",'子进程的id为:',os.getpid())


    #创建五个进程
    if __name__ == '__main__':
        # 创建管道对象
        # 管道函数返回了两个对象
        child_conn,parent_conn = Pipe()
        job = []
        for i in range(5):
            p = Process(target=func,args=(i,child_conn))
            #把新的进程添加到列表里
            job.append(p)
            p.start()
        #从管道中接收,此时写在了join前面,意味着这个for循环时和所有子进程同步进行的
        for i in range(5):
            data = parent_conn.recv()
            print(data)
        for i in job:
            i.join()

                  打印结果为:

                            博主最帅2
                            父进程的id为: 12048 -------- 子进程的id为: 12232
                            博主最帅3
                            博主最帅1
                            父进程的id为: 12048 -------- 子进程的id为: 8168
                            父进程的id为: 12048 -------- 子进程的id为: 9708
                            博主最帅4
                            父进程的id为: 12048 -------- 子进程的id为: 3984
                            博主最帅0

                            父进程的id为: 12048 -------- 子进程的id为: 11712

               解析:一个父进程,创建了5个子进程,5个子进程都往管道里写数据,父进程这边做接收(5个子进程往进写,就要接收5次,所以for i in range(5):data=xxx),把接收写在了join前面,意味着接收这个循环时和所有子进程同步进行。那么有个问题,在睡眠期间,这个父进程有没有执行,答案时当然在执行,那为什么没有打印出来呢?不打印说明了父进程遇到了阻塞,在parent_conn.recv()这阻塞了,recv()是阻塞函数,recv()是要从管道里往出拿东西,但是呢,子进程往进放东西前一直在睡眠,那么主进程就一直从往下执行,直到data部分,管道是空的,这个时候他就阻塞了,所以recv是一个阻塞函数(当管道为空的时候就会阻塞)。当睡眠时间过去后,5个子进程同步执行,都往管道里放东西,谁先放,谁后放这个不一定,下面主进程这块也是同步执行的,随时接收到东西,随时打印出来,这就是为什么运行结果是这样的

               2.把上面代码稍作修改:

import os,time
    from multiprocessing import Pipe,Process

    #进程函数()
    #a为形参,代表着child_conn
    def func(name,a):
        time.sleep(1)
        #把字符串往管道里发送
        a.send('博主最帅'+str(name))
        print('父进程的id为:',os.getppid(),"--------",'子进程的id为:',os.getpid())


    #创建五个进程
    if __name__ == '__main__':
        # 创建管道对象
        # 管道函数返回了两个对象
        child_conn,parent_conn = Pipe()
        job = []
        for i in range(5):
            p = Process(target=func,args=(i,child_conn))
            #把新的进程添加到列表里
            job.append(p)
            p.start()
        #从管道中接收,此时写在了join前面,意味着这个for循环时和所有子进程同步进行的
        #for i in range(5):
        time.sleep(5)
        data = parent_conn.recv()
        print(data)
        for i in job:
            i.join()


打印结果为:


                                父进程的id为: 11916 -------- 子进程的id为: 15404
                                父进程的id为: 11916 -------- 子进程的id为: 11852
                                父进程的id为: 11916 -------- 子进程的id为: 1648
                                父进程的id为: 11916 -------- 子进程的id为: 4736
                                父进程的id为: 11916 -------- 子进程的id为: 15588

                                博主最帅3

                    上面子进程睡眠了1秒钟,5个子进程都把东西放到了管道里,也就是5个子进程在一秒之后全部执行完毕,主进程睡眠5秒之后,只接收一次就出现了上面的打印结果,只接收到了一个,你从管道里send进去5个内容,但是我只有一个recv()函数,就只接收一次


4.总结:

            1.向管道发送数据使用send函数,从管道接收数据使用recv()函数

            2.recv()函数为阻塞函数,当管道中数据为空的时候会阻塞

            3.一次recv()只能接收一次send()的内容

            4.send可以发送的数据类型比较多样,字符串,数字,列表等等