多进程

进程

一个程序运行起来后,代码 + 所用到的资源 称之为进程,它是操作系统分配资源的基本单元

一个静止的程序 .exe 或者 .py 不能成为进程,只有它执行起来,一旦被拿到运行内存里面去,执行代码就需要占用资源(包括 网络的占用,CPU的占用,摄像头的占用,等等。。。)

线程是执行代码的东西,是依附于进程的

不仅仅通过线程完成多任务,进程也是可以的

进程的状态(生命周期)

工作中,任务数往往大于CPU的核数,即 一定有一些任务正在执行,而另外一些任务正在等待CPU进行执行,因此导致了有不同的状态

5个状态:

在这里插入图片描述

  • 就绪状态:运行的条件都已经慢去,正在等待CPU执行
  • 执行状态:CPU正在执行某功能
  • 等待状态:等待某些条件满足,例如 一个程序 sleep 了,此时就处于等待状态
import multiprocessing,time

def sing():
    for i in range(3):
        print('唱歌。。。')
        time.sleep(1)

def dance():
    for i in range(3):
        print('跳舞。。。')
        time.sleep(1)

def main():
    # 利用多进程去完成多任务
    p1 = multiprocessing.Process(target=sing) # 创建子进程对象
    p2 = multiprocessing.Process(target=dance) # 创建子进程对象
    p1.start()  # 调用start方法,启动子线程
    p2.start()

if __name__ =='__main__':
    main()

在这里插入图片描述

既然是一个多进程的任务,如何查看进程号呢? – OS模块

import multiprocessing,time
import os

# 证明多进程  就去查看它的进程号
def sing():  # 子进程A 执行
    for i in range(3):
        print('唱歌。。。执行它的进程号是:',os.getpid())
        time.sleep(1)

def dance(): # 子进程B 执行
    for i in range(3):
        print('跳舞。。。执行它的进程号是:',os.getpid())
        time.sleep(1)

def main():  # 主进程来执行
    # 利用多进程去完成多任务
    p1 = multiprocessing.Process(target=sing) # 创建子进程对象
    p2 = multiprocessing.Process(target=dance) # 创建子进程对象
    p1.start()  # 调用start方法,启动子线程
    p2.start()
    time.sleep(1)
    print('主进程的PID,进程号是:',os.getpid())

if __name__ =='__main__':
    main()

在这里插入图片描述
查看父进程pid:

print('父进程的pid',os.getppid())

在这里插入图片描述
在这里插入图片描述
多线程的体现方式:在一个进程内,多个线程分别去执行代码,达到多任务的目的

多进程的体现方式:一个子进程就拥有一份代码,里面就有一个主线程去执行,多个进程就实现多任务

如果进程是一条流水线的话,那每个工人就是一个线程
在这里插入图片描述

如果代码没有类似全局变量的自增操作,那么多进程会共享一份代码,,,

在这里插入图片描述

总结:多任务、线程占用的资源小,进程占用的资源大

多进程之间不能共享全局变量,这和多线程不同

import multiprocessing,time

list = [1,2,3]

def demo1():
    list.append(4)
    print('在demo1函数中,list的值是',list)

def demo2():
    list.append(5)
    print('在demo2函数中,list的值是',list)

def main():
    p1 = multiprocessing.Process(target=demo1)  # 子进程A准备就绪
    p2 = multiprocessing.Process(target=demo2)  # 子进程B准备就绪 
    p1.start()     # 预测结果:[1,2,3,4]
    time.sleep(1)  # 为了让上面的进程先执行完,时间的浪费
    p1.join()   # 就能确保p1进程执行完毕
    p2.start()     # 预测结果:[1,2,3,4,5]
    p2.join()   # 堵塞效果,确保该进程执行完毕 超时等待 >>> 5s过后继续往下走
    time.sleep(1)

    list.append(6)  # 预测结果:[1,2,3,4,5,6]
    print('在main函数中,list的值是:',list)

if __name__=='__main__':
    main()

在这里插入图片描述

  • 进程是系统进行资源(CPU 网络 运行内存空间 摄像头)分配和调度的一个独立单位,基本单位
  • 线程是进程的一个实体,是CPU调度和分派的基本单位,是比进程更小的能独立运行的基本单位,线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如 程序计算器、一组寄存器 和 栈),但是它可以同属于一个进程的其它线程共享进程所拥有的全部资源
  • 一个程序至少有一个进程,一个进程至少有一个线程
  • 线程的划分尺度小于进程(资源比进程少),使得多线程程序的并发性高,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率
  • 线程不能独立执行,必须依存在进程中

进程之间是相互独立存在的(空间不同)>> 不能资源共享,不能信息传递

比如爬虫问题的多线程操作多任务,但是在多进程之间不能实现资源共享,那怎么办呢?

于是,出来的 Queue队列,用于进程之间的通信

在这里插入图片描述
在硬盘中不会存在多进程的情况,只有在运行内存中,程序运行起来时才会有多进程一说,多进程之间的通信依靠 Queue队列

四个方法:

import multiprocessing
q = multiprocessing.Queue(3)  # 划分空间,可以自己去设置队列容器的大小,只能存放3个数据

	# 如何往对象里面放数据
q.put() # 函数1、方法2

	# 如何查看队列里面已经满了
q.full() # 如果满了就返回一个True,否则 False

	# 只进不出的,获取队列里面的数据
q.get()

	# 判断队列是否为空
q.empty()  # True False

例子:

import multiprocessing
q = multiprocessing.Queue(3)

q.put(123)
q.put('你好')
q.put([1,2,3])

# 往队列里面取数据
print('队列里面取出来的第一个数据是:',q.get())
print('队列里面取出来的第二个数据是:',q.get())
print('队列里面取出来的第三个数据是:',q.get())

队列是,单独的容器,作为一个通道,交流的桥梁,可以让进程之间完成通信;并且尽可能的降低耦合度

import multiprocessing

def demo1(q):  # 提取(创造数据)
    """模拟下载数据"""
    download_data = [i for i in range(1,11)]
    # 把下载好的数据分别放进队列当中
    for i in download_data:
        q.put(i)
        print('放进队列的数据是:',i)
    print('数据已经全部放进了队列当中......')

def demo2(q):  # 清洗数据
    """模拟清洗数据"""
    # 只想要偶数,把它放进一个列表,打印出来
    list1 = []
    while True:
        i = q.get()  # 只拿一个数据,先进先得
        if i % 2 == 0:   # 只要是偶数
            list1.append(i)  # 那么就放到列表里面去
            print('此时此刻的list1里面是:',list1)
        # 如果队列为空,那么就跳出循环
        if q.empty():
            break
        print('清洗完的数据是:',list1)

def main():
    # 创建一个队列
    q = multiprocessing.Queue()
    # 创建两个子进程,进程A提取(创造数据),子进程B清洗数据
    p1 = multiprocessing.Process(target=demo1,args=(q,))
    p2 = multiprocessing.Process(target=demo2,args=(q,))
    p1.start()
    p2.start()

if __name__=='__main__':
    main()

在这里插入图片描述

进程池

进程池是一个特殊的容易,可以容纳一定数量的进程 >> 完成任务

重复利用进程池里面的进程,完成多个任务的目的
100任务
进程池 5 进程 节约大量的资源 重复利用进程来完成多个任务,减轻操作系统调度的压力

创建一个进程池 >>

import multiprocessing
import os,time,random

def demo(msg):
    t_start = time.time()
    print('%s开始执行,进程号为%d'%(msg,os.getpid()))
    # random.random()随机生成0-1之间的浮点数
    time.sleep(random.random() * 2)  # 随机数字,让程序休眠挂起
    t_stop = time.time()  # 记录一下此时的时间
    print(msg,'执行完毕,耗时%0.2f'%(t_stop - t_start)) # 执行一个任务所用到的时间

if __name__ == '__main__':
    p0 = multiprocessing.Pool(3) # 定义一个进程池,最大进程数为3
    for i in range(10):
        # 实例对象.apply_async(要调用的目标,传递给目标的参数元组,)
        # 每次循环将会用空闲出来的子进程去调用目标
        p0.apply_async(demo,(i,)) # 启动进程池里的进程去执行任务

    print('-----start-----')
    p0.close()  # 关闭进程池,关闭后p0不再接收新的请求
    p0.join()   # 阻塞操作!等待p0所有子进程执行完成,必须放在close语句之后
    print('-----end-----')

在这里插入图片描述
利用进程池里面的进程,重复完成任务,,,减少了大量的创建和释放进程资源的过程,大大提高了效率

GIL (全局解释锁)

  • 1、GIL导致线程不能利用多核优势
  • 2、多进程实现方式(2)
  • 3、t is join daemon name pid
  • 4、锁
  • 5、进程间通信
  • 6、进程池

1.计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行。

2.假定工厂的电力有限,一次只能供给一个车间使用。也就是说,一个车间开工的时候,其他车间都必须停工。背后的含义就是,单个CPU一次只能运行一个任务。

3.进程就好比工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。

4.一个车间里,可以有很多工人。他们协同完成一个任务。

5.线程就好比车间里的工人。一个进程可以包括多个线程。

6.车间的空间是工人们共享的,比如许多房间是每个工人都可以进出的。 这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享内存。

7.可是,每间房间的大小不同,有些房间最多只能容纳一个人,比如厕所。里面有人的时候,其他人就不能进去了。这代表一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。

8.一个防止他人进入的简单方法,就是门口加一把锁。先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去。这就叫互斥锁(Mutual exclusion,缩写 Mutex), 防止多个线程同时读写某一块内存区域。

9.还有些房间,可以同时容纳n个人,比如厨房。也就是说,如果人数大于n,多出来的人只能在外面等着。这好比某些内存区域,只能供给固定数目的线程使用。

10.这时的解决方法,就是在门口挂n把钥匙。进去的人就取一把钥匙,出来时再把钥匙挂回原处。 后到的人发现钥匙架空了,就知道必须在门口排队等着了。这种做法叫做信号量(Semaphore), 用来保证多个线程不会互相冲突。不难看出,mutexsemaphore 的一种特殊情况(n=1时)。也就是说,完全可以用后者替代前者。

但是,因为 mutex 较为简单,且效率高,所以在必须保证资源独占的情况下,还是采用这种设计。

操作系统的设计,因此可以归结为三点:
  • (1)以多进程形式,允许多个任务同时运行;
  • (2)以多线程形式,允许单个任务分成不同的部分运行;
  • (3)提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源。
GIL 全局解释器锁:

同一进程下的多线程共享数据,共享意味着竞争,竞争带来无序,为了数据安全所以需要加锁进行
数据保护,GIL本质是一把互斥锁,使并发变为串行,保证同一时间只有一条线程访问解释器级别的
数据,这样就保证了解释器级别的数据安全,**同时也带来了一些问题,同一进程只有一条线程执行任务,**无法利用多核优势,解决方案可以根据任务的类型来处理,如果是I/O密集型,则需要开多线程提高效率如果是计算密集型则需要多进程
IO操作不占用CPU 计算密集型的操作不适合使用多线程
IO密集型不适合使用多线程

在这里插入图片描述
如上图示意,一个进程相当于一个工厂里加工产品,工人有很多,相当于多线程,但是加工的机器只有一台,相当于一个CPU,一个工人操作机器的时候,其它人只能等着他把机器用完,在排队上,这样反而降低了效率。

1.multiprocessing模块介绍

python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),
在python中大部分情况需要使用多进程。Python提供了非常好用的多进程包multiprocessing。
multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),
该模块与多线程模块 threading 的编程接口类似。
multiprocessing模块的功能众多:支持子进程、通信和共享数据、
执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。
需要再次强调的一点是:与线程不同,进程没有任何共享状态,进程修改的数据,改动仅限于该进程内。

import multiprocessing

def run(name):
    print("来了",name)
    
if __name__ == '__main__':      #  这是和线程不同的一个地方,下面的三个进程需要放在这条语句下面

p1 = multiprocessing.Process(target=run,kwargs={"name":"老弟1"})
p2 = multiprocessing.Process(target=run,kwargs={"name":"老弟2"})
p3 = multiprocessing.Process(target=run,kwargs={"name":"老弟3"})

# 下面截图说明,上面三个进程像线程一样运行 

p1.start()
p2.start()
p3.start()

在这里插入图片描述

2.Process类的介绍

创建进程的类
Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,
表示一个子进程中的任务(尚未启动)

强调:
  1. 需要使用关键字的方式来指定参数
  2. args 指定的为传给 target函数 的位置参数,是一个元组形式必须有逗号
    参数介绍
    group参数未使用,值始终为None
    target表示调用对象,即子进程要执行的任务
    args表示调用对象的位置参数元组,args=(1,2,‘hexin’,)
    kwargs表示调用对象的字典,kwargs={‘name’:‘hexin’,‘age’:18}
    name为子进程的名称

可以给进程起名字的:

import multiprocessing

def run(name):
     print("来了",name)
if __name__ == '__main__':

p1 = multiprocessing.Process(target=run,kwargs={"name":"老弟1"},name="张三")
p2 = multiprocessing.Process(target=run,kwargs={"name":"老弟2"},name="李四")
p3 = multiprocessing.Process(target=run,kwargs={"name":"老弟3"},name="王麻子")

p1.start()
p2.start()
p3.start()

print(p1.name)
print(p2.name)
print(p3.name)

在这里插入图片描述
p.start(): 启动进程,并调用该子进程中的p.run()
p.run(): 进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
p.terminate(): 强制终止进程 p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,
使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁

一个CPU只能运行一个进程,这是铁定的事实

三个进程和三个线程不一样,三个进程分别是一个车间,每个进程一个CPU执行互不干扰,那互不干扰怎么让他们仨产生关系呢?。。。。可以弄一个公共的数据,把这个公共的数据上一把锁,每个进程都要执行公共数据,但一次只能一个进程去执行,一个进程进去操作了,另外两个只有等着它完事的!这就达到了死锁 的效果;
它和线程死锁不同,线程死锁是大家都在运行一个对象,但进程是在各自的车间干自己的活,各进程互不干扰,所以进程要达到死锁的效果,必须创造一个公共的被执行数据,锁上这个公共数据,就完成死锁

import multiprocessing

def run(name):
    print("来了",name)
if __name__ == '__main__':
p1 = multiprocessing.Process(target=run,kwargs={"name":"老弟1"})
p2 = multiprocessing.Process(target=run,kwargs={"name":"老弟2"})
p3 = multiprocessing.Process(target=run,kwargs={"name":"老弟3"})

p1.start()
p2.start()
p3.start()

p1.terminate()   # p1被死锁,变成了僵尸进程

在这里插入图片描述

p.is_alive(): 如果p仍然运行,返回True(判断进程是不是还活着)
在这里插入图片描述
p.join([timeout]): 主线程等待p终止
强调:1.是主线程处于等的状态,而p是处于运行的状态;2. 挂起的是主线程)。

import multiprocessing,time
def run(name):
#time.sleep(5)
print("来了",name)
if __name__ == '__main__':

p1 = multiprocessing.Process(target=run,kwargs={"name":"老弟1"})
p2 = multiprocessing.Process(target=run,kwargs={"name":"老弟2"})
p3 = multiprocessing.Process(target=run,kwargs={"name":"老弟3"})

p1.start()
p2.start()
p3.start()

p2.join()    # 挂起主线程, 等p2结束后,再正常运行
print("主线程结束")

在这里插入图片描述

timeout 是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,
而不能join住run开启的进程

p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,
p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
p.name: 进程的名称
p.pid:进程的pid
p.exitcode: 进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
p.authkey: 进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。
这个键的用途是为涉及网络连接的底层进程间通信提供安全性,
这类连接只有在具有相同的身份验证键时才能成功(了解即可)

3.Process类的使用
创建并开启子进程的两种方式
方法1 —— 函数式
import time
import random
from multiprocessing import Process
def piao(name):
    print('%s piao' %name)
    time.sleep(random.randrange(1,5))
    print('%s piao end' %name)

p1=Process(target=piao,args=('e',)) #必须加,号
p2=Process(target=piao,args=('a',))
p3=Process(target=piao,args=('w',))
p4=Process(target=piao,args=('y',))

p1.start()
p2.start()
p3.start()
p4.start()
print('主线程')

输出
e piao
主线程
a piao
w piao
y piao
e piao end
y piao end
a piao end
w piao end

import multiprocessing

class MyProcess(multiprocessing.Process):

def __init__(self):
    multiprocessing.Process.__init__(self)
def run(self):
    print("来了,老弟")
    
if __name__=="__main__":      # 除了多这一条语句,其他的和咱们写多线程的代码一样
# 创建两个进程:
p1 = MyProcess()
p2 = MyProcess()

p1.start()
p2.start()

在这里插入图片描述

方法2 —— 继承式
import time
import random
from multiprocessing import Process

class Piao(Process):
def __init__(self,name):
    super().__init__()
    self.name=name
def run(self):
    print('%s piaoing' %self.name)

    time.sleep(random.randrange(1,5))
    print('%s piao end' %self.name)

p1=Piao('e')
p2=Piao('a')
p3=Piao('w')
p4=Piao('y')

p1.start()  # start会自动调用run
p2.start() 
p3.start()
p4.start()
print('主线程')

输出

e piaoing
主线程
a piaoing
w piaoing
y piaoing
e piao end
a piao end
y piao end
w piao end

注意:在windows中Process()必须放到# if __name__ == '__main__':下
Process对象的其他方法或属性
#进程对象的其他方法一:terminate,is_alive

from multiprocessing import Process
import time
import random
class Piao(Process):
def __init__(self,name):
    self.name=name
    super().__init__()

def run(self):
    print('%s is piaoing' %self.name)
    time.sleep(random.randrange(1,5))
    print('%s is piao end' %self.name)

p1=Piao('e1')
p1.start()

p1.terminate()        # 关闭进程,不会立即关闭,所以is_alive立刻查看的结果可能还是存活
print(p1.is_alive())  # 结果为True

print('开始')
print(p1.is_alive())  # 结果为False

输出

True
开始
False

#进程对象的其他方法二:p.daemon=True,p.join

from multiprocessing import Process
import time
import random

class Piao(Process):
def __init__(self,name):
    self.name=name
    super().__init__()
def run(self):
    print('%s is piaoing' %self.name)
    time.sleep(random.randrange(1,3))
    print('%s is piao end' %self.name)


p=Piao('e')
p.daemon=True  # 一定要在p.start()前设置,设置p为守护进程,禁止p创建子进程,并且父进程死,p跟着一起死
p.start()
p.join(0.0001) # 等待p停止,等0.0001秒就不再等了
print('开始')

输出

Piao-1 is piaoing
开始

注意:p.join(),是父进程在等p的结束,是父进程阻塞在原地,而p仍然在后台运行

进程对象的其他属性:name,pid

from multiprocessing import Process
import time
import random
class Piao(Process):
def __init__(self,name):
    # self.name=name
    # super().__init__() #Process的__init__方法会执行self.name=Piao-1,
    #                    #所以加到这里,会覆盖我们的self.name=name

    #为我们开启的进程设置名字的做法
    super().__init__()
    self.name=name

def run(self):
    print('%s is piaoing' %self.name)
    time.sleep(random.randrange(1,3))
    print('%s is piao end' %self.name)

p=Piao('e')
p.start()
print('开始')
print(p.pid) #查看pid
4.进程同步(锁)

进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的。
共享同一打印终端,发现会有多行内容打印到一行的现象(多个进程共享并抢占同一个打印终端,乱了)
既然可以用文件共享数据,那么进程间通信用文件作为数据传输介质就可以了啊,可以,但是有问题:

1.效率
2.需要自己加锁处理

加锁的目的是为了保证多个进程修改同一块数据时,同一时间只能有一个修改,即串行的修改,
没错,速度是慢了,牺牲了速度而保证了数据安全。

上述加锁的思路,即是 对多进程进行死锁的方法,找一个几个进程都需要共享公共数据,对公共数据进行加锁

文件当做数据库,模拟抢票(Lock互斥锁)

import multiprocessing,time
Lock = multiprocessing.Lock()  # 创建一把锁
list = []
for i in range(100):
     list.append(i)

def work(name):
while len(list)>0:
    print(list.pop(),name)

if __name__ == '__main__':

p1 = multiprocessing.Process(target=work,args=("p1",))
p2 = multiprocessing.Process(target=work,args=("p2",))
p3 = multiprocessing.Process(target=work,args=("p3",))
p1.start()
p2.start()
p3.start()

r  只能读  rb 读字节   r+ 读写    rb+ 
w   wb    w+    wb+
a   ab    a+    wb+ 

import multiprocessing,time
Lock = multiprocessing.Lock()  # 创建一把锁

def work(name):
    with open("b.txt",mode="r+",encoding="utf-8") as f:    # r+ 是追加的
    c = int(f.read())
    while c>0:
       c -= 1
       f.write(str(c))
       time.sleep(0.1)

if __name__ == '__main__':

 p1 = multiprocessing.Process(target=work,args=("p1",))
 p2 = multiprocessing.Process(target=work,args=("p2",))
 p3 = multiprocessing.Process(target=work,args=("p3",))
 
 p1.start()
 p2.start()
 p3.start()

在这里插入图片描述
b.txt的原文本是“1000”,进程执行后依次减1——999、998…
在这里插入图片描述

#文件db的内容为:{"count":2}
#注意一定要用双引号,不然json无法识别

from multiprocessing import Process,Lock
import json
import time
import random
import os

def work(filename,lock):    # 买票
# lock.acquire()
with lock:
    with open(filename,encoding='utf-8') as f:
        dic=json.loads(f.read())
        # print('剩余票数: %s' % dic['count'])
    if dic['count'] > 0:
        dic['count']-=1
        time.sleep(random.randint(1,3))     # 模拟网络延迟
        with open(filename,'w',encoding='utf-8') as f:
            f.write(json.dumps(dic))
        print('%s 购票成功' %os.getpid())
    else:
        print('%s 购票失败' %os.getpid())
# lock.release()

if __name__ == '__main__':
lock=Lock()    # 在主线程里创建了一把锁
p_l=[]
for i in range(10):
    p=Process(target=work,args=('db',lock))   # 创建这个进程时候可以把锁传过去
    p_l.append(p)
    p.start()
for p in p_l:
    p.join()

print('主线程')

输出

7932 购票成功
7933 购票成功
7934 购票失败
7935 购票失败
7936 购票失败
7937 购票失败
7938 购票失败
7939 购票失败
7940 购票失败
7941 购票失败
主线程

5.进程间通信(IPC)[方式有两种]
方式一:队列(推荐使用)

队列先进先出,栈后进先出

创建队列的类(底层就是以管道 和 锁定的方式实现):

Queue([maxsize]) : 创建共享的进程队列,Queue是多进程安全的队列,
可以使用Queue实现多进程之间的数据传递。
参数介绍
maxsize 是队列中允许最大项数,省略则无大小限制。

方法介绍:

q.put方法 用以插入数据到队列中
put方法还有两个可选参数:blockedtimeout

  1. 如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间。
  2. 如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。

q.get方法可以从队列读取并且删除一个元素。

get方法有两个可选参数:blockedtimeout

  1. 如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。
  2. 如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常.

q.get_nowait() : 同q.get(False)
q.put_nowait() : 同q.put(False)

q.empty(): 调用此方法时q为空则返回True,该结果不可靠,比如在返回True的过程中,如果队列中又加入了项目。

q.full() :调用此方法时q已满则返回True,该结果不可靠,比如在返回True的过程中,如果队列中的项目被取走。

q.qsize() : 返回队列中目前项目的正确数量,结果也不可靠,理由同 q.empty() 和 q.full() 一样

multiprocessing模块支持进程间通信的两种主要形式:管道和队列
都是基于消息传递实现的
,但是队列接口

import multiprocessing,time

def work(q):
    if not q.empty():
    print(q.get())

if __name__ == '__main__':

q = multiprocessing.Queue(maxsize=3)
q.put(1)
q.put(2)
q.put(3)

p1 = multiprocessing.Process(target=work,args=(q,))
p2 = multiprocessing.Process(target=work,args=(q,))

p1.start()
p2.start()

在这里插入图片描述

from multiprocessing import Process,Queue
import time
q=Queue(3)

#put ,get ,put_nowait,get_nowait,full,empty
q.put(3)
q.put(3)
q.put(3)
print(q.full()) #满了

print(q.get())
print(q.get())
print(q.get())
print(q.empty()) #空了

输出
True
3
3
3
True

方式二:管道

创建管道的类:
Pipe([duplex]):在进程之间创建一条管道,并返回元组(conn1,conn2),
其中conn1,conn2表示管道两端的连接对象,强调一点:必须在产生Process对象之前产生管道

参数介绍:
dumplex:默认管道是全双工的,如果将duplex射成False,conn1只能用于接收,conn2只能用于发送。

方法介绍:
主要方法:
conn1.recv() : 接收conn2.send(obj)发送的对象。如果没有消息可接收,recv方法会一直阻塞。

如果连接的另外一端已经关闭,那么recv方法会抛出EOFError。

conn1.send(obj) : 通过连接发送对象。obj是与序列化兼容的任意对象

conn1.close() : 关闭连接。如果conn1被垃圾回收,将自动调用此方法

conn1.fileno() : 返回连接使用的整数文件描述符

conn1.poll([timeout]) : 如果连接上的数据可用,返回True。timeout指定等待的最长时限。如果省略此参数,方法将立即返回结果。如果将timeout射成None,操作将无限期地等待数据到达。

conn1.recv_bytes([maxlength]) : 接收c.send_bytes()方法发送的一条完整的字节消息。maxlength指定要接收的最大字节数。如果进入的消息,超过了这个最大值,将引发IOError异常,并且在连接上无法进行进一步读取。如果连接的另外一端已经关闭,再也不存在任何数据,将引发EOFError异常。
conn.send_bytes(buffer [, offset [, size]]) :通过连接发送字节数据缓冲区,buffer是支持缓冲区接口的任意对象,offset是缓冲区中的字节偏移量,而size是要发送字节数。结果数据以单条消息的形式发出,然后调用c.recv_bytes()函数进行接收

conn1.recv_bytes_into(buffer [, offset]) : 接收一条完整的字节消息,并把它保存在buffer对象中,该对象支持可写入的缓冲区接口(即bytearray对象或类似的对象)。offset指定缓冲区中放置消息处的字节位移。返回值是收到的字节数。如果消息长度大于可用的缓冲区空间,将引发BufferTooShort异常。

import multiprocessing
#         接收方=======================发送方
def jieshou(left,right):
  # right.close()
    while True:
       print(left.recv())
       left.send("你好")

def fasong(left,right):
   # left.close()
    while True:
       right.send("我是被发送出去的数据")
       print(right.recv())

if __name__ == '__main__':

   left,right = multiprocessing.Pipe()
   p1 = multiprocessing.Process(target=jieshou,args=(left,right))  # 这个进程 调用接收
   p2 = multiprocessing.Process(target=fasong,args=(left,right))  # 这个进程 调用发送
   p1.start()
   p2.start()

在这里插入图片描述

基于管道实现进程间通信(与队列的方式是类似的,队列就是管道加锁实现的):

from multiprocessing import Process,Pipe

import time,os
def consumer(p,name):
   left,right=p
   left.close()
while True:
    try:
        baozi=right.recv()
        print('%s 收到包子:%s' %(name,baozi))
    except EOFError:
        right.close()
        break

def producer(seq,p):
    left,right=p
    right.close()
for i in seq:
    left.send(i)
    # time.sleep(1)
else:
    left.close()

if __name__ == '__main__':
left,right=Pipe()

c1=Process(target=consumer,args=((left,right),'c1'))
c1.start()
seq=(i for i in range(10))
producer(seq,(left,right))

right.close()
left.close()

c1.join()
print('主进程')

输出

c1 收到包子:0
c1 收到包子:1
c1 收到包子:2
c1 收到包子:3
c1 收到包子:4
c1 收到包子:5
c1 收到包子:6
c1 收到包子:7
c1 收到包子:8
c1 收到包子:9
主进程

注意:生产者和消费者都没有使用管道的某个端点,就应该将其关闭,如在生产者中关闭管道的右端,在消费者中关闭管道的左端。如果忘记执行这些步骤,程序可能再消费者中的recv()操作上挂起。管道是由操作系统进行引用计数的,必须在所有进程中关闭管道后才能生产EOFError异常。因此在生产者中关闭管道不会有任何效果,付费消费者中也关闭了相同的管道端点。

管道可以用于双向通信,利用通常在客户端/服务器中使用的请求/响应模型或远程过程调用,就可以使用管道编写与进程交互的程序,如下

from multiprocessing import Process,Pipe

import time,os
def adder(p,name):
   server,client=p
   client.close()
while True:
    try:
        x,y=server.recv()
    except EOFError:
        server.close()
        break
    res=x+y
    server.send(res)
print('server done')

if __name__ == '__main__':
server,client=Pipe()

c1=Process(target=adder,args=((server,client),'c1'))
c1.start()

server.close()

client.send((10,20))
print(client.recv())
client.close()

c1.join()
print('主进程')

输出

30
server done
主进程
注意:send()和recv()方法使用pickle模块对对象进行序列化。

6.生产者消费者模型
什么是生产者消费者模式?

生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。
生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,
所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,
消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,
平衡了生产者和消费者的处理能力。

为什么要使用生产者和消费者模式

在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,
如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,
才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。
为了解决这个问题于是引入了生产者和消费者模式。
在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。
该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。

基于队列实现生产者消费者模型

from multiprocessing import Process,Queue
import time,random,os

def consumer(q):
while True:
    time.sleep(random.randint(1,3))
    res=q.get()
    if res is None:break
    print('\033[45m消费者拿到了:%s\033[0m' %res)

def producer(seq,q):
for item in seq:
    time.sleep(random.randint(1,3))
    print('\033[46m生产者生产了:%s\033[0m' %item)

    q.put(item)

if __name__ == '__main__':
q=Queue()

c=Process(target=consumer,args=(q,))
c.start()

producer(('包子%s' %i for i in range(5)),q)
q.put(None)
c.join()
print('主线程')

输出

生产者生产了:包子0
消费者拿到了:包子0
生产者生产了:包子1
消费者拿到了:包子1
生产者生产了:包子2
消费者拿到了:包子2
生产者生产了:包子3
消费者拿到了:包子3
生产者生产了:包子4
消费者拿到了:包子4
主线程

创建队列的另外一个类
JoinableQueue([maxsize]):这就像是一个Queue对象,
但队列允许项目的使用者通知生成者项目已经被成功处理。
通知进程是使用共享的信号和条件变量来实现的。
maxsize是队列中允许最大项数,省略则无大小限制。

JoinableQueue的实例p除了与Queue对象相同的方法之外还具有:

q.task_done() :使用者使用此方法发出信号,表示q.get()的返回项目已经被处理。
如果调用此方法的次数大于从队列中删除项目的数量,将引发ValueError异常
q.join() : 生产者调用此方法进行阻塞,直到队列中所有的项目均被处理。
阻塞将持续到队列中的每个项目均调用q.task_done()方法为止

from multiprocessing import Process,JoinableQueue
import time,random
def consumer(q):
while True:
    # time.sleep(random.randint(1,2))
    res=q.get()
    print('消费者拿到了 %s' %res)
    q.task_done()


def producer(seq,q):
for item in seq:
    # time.sleep(random.randrange(1,2))
    q.put(item)
    print('生产者做好了 %s' %item)
q.join()

if __name__ == '__main__':
q=JoinableQueue()
seq=('包子%s' %i for i in range(5))

p=Process(target=consumer,args=(q,))
p.daemon=True #设置为守护进程,在主线程停止时p也停止,但是不用担心,producer内调用q.join保证了consumer已经处理完队列中的所有元素
p.start()
producer(seq,q)
print('主线程')

输出

生产者做好了 包子0
生产者做好了 包子1
生产者做好了 包子2
生产者做好了 包子3
生产者做好了 包子4
消费者拿到了 包子0
消费者拿到了 包子1
消费者拿到了 包子2
消费者拿到了 包子3
消费者拿到了 包子4
主线程

8. 进程池

开多进程的目的是为了并发,如果有多核,通常有几个核就开几个进程,进程开启过多,效率反而会下降(开启进程是需要占用系统资源的,而且开启多余核数目的进程也无法做到并行),但很明显需要并发执行的任务要远大于核数,这时我们就可以通过维护一个进程池来控制进程数目,
比如 httpd 的进程模式,规定最小进程数和最大进程数…

当被操作对象数目不大时,可以直接利用 multiprocessing 中的Process动态成生多个进程,十几个还好,但如果是上百个,上千个目标,手动的去限制进程数量却又太过繁琐,此时可以发挥进程池的功效。

其实可以利用Python 查看电脑CPU核数的:

import os
print(os.cpu_count())

在这里插入图片描述
而且对于远程过程调用的高级应用程序而言,应该使用进程池,Pool可以提供指定数量的进程,供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;
但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,就重用进程池中的进程。

在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间。

创建进程池的类:
Pool([numprocess [,initializer [, initargs]]]) :创建进程池

参数介绍:
numprocess : 要创建的进程数,如果省略,将默认使用cpu_count()的值
initializer : 是每个工作进程启动时要执行的可调用对象,默认为None
initargs :是要传给initializer的参数组

方法介绍:

p.apply(func [, args [, kwargs]]) :
在一个池工作进程中执行func(*args,**kwargs),然后返回结果。需要强调的是:此操作并不会在所有池工作进程中并执行func函数。如果要通过不同参数并发地执行func函数,必须从不同线程调用p.apply()函数或者使用

p.apply_async()
p.apply_async(func [, args [, kwargs]]) :
在一个池工作进程中执行func(*args,**kwargs),然后返回结果。此方法的结果是AsyncResult类的实例,callback是可调用对象,接收输入参数。当func的结果变为可用时,将理解传递给callback。
callback禁止执行任何阻塞操作,否则将接收其他异步操作中的结果。

p.close() :
关闭进程池,防止进一步操作。如果所有操作持续挂起,它们将在工作进程终止前完成5 P.jion():等待所有工作进程退出。此方法只能在close()或teminate()之后调用
方法apply_async()map_async() 的返回值是AsyncResul的实例obj。
实例具有以下方法
obj.get():返回结果,如果有必要则等待结果到达。timeout是可选的。如果在指定时间内还没有到达,将引发一场。如果远程操作中引发了异常,它将在调用此方法时再次被引发。

obj.ready() : 如果调用完成,返回True
obj.successful() : 如果调用完成且没有引发异常,返回True,如果在结果就绪之前调用此方法,引发异常
obj.wait([timeout]) : 等待结果变为可用。
obj.terminate() :立即终止所有工作进程,同时不执行任何清理或结束任何挂起工作。如果p被垃圾回收,将自动调用此函数

应用

提交任务,并在主进程中拿到结果(之前的Process是执行任务,结果放到队列里,现在可以在主进程中直接拿到结果)

from multiprocessing import Pool
import time
def work(n):   # 写一个work方法
   print('开工啦...')
   time.sleep(3)
  return n**2 # 返回n的平方

if __name__ == '__main__':
q = Pool()   # 创建一个进程池

# 异步apply_async用法:如果使用异步提交的任务,主进程需要使用jion,等待进程池内任务都处理完,
然后可以用get收集结果,否则,主进程结束,进程池可能还没来得及执行,也就跟着一起结束了

res=q.apply_async(work,args=(2,))  # res 是为接收数值的
q.close()
q.join()  # join在close之后调用,因为主进程结束了,其它进程还没结束,获取数据肯定获取不到,所以一定要挂起一下,等一下
print(res.get()) 

# 同步apply用法:主进程一直等apply提交的任务结束后才继续执行后续代码
# res=q.apply(work,args=(2,))    
# print(res)

同步q.apply异步q.apply_async 的区别是:
主进程会等appiy提交的任务结束后才继续执行后续代码,而异步不会等,所以需要join挂起一下

输出

开工啦…
4

详解:apply_async与apply

#一:使用进程池(非阻塞,apply_async)
#coding: utf-8

from multiprocessing import Process,Pool
import time

def func(msg):
    print( "msg:", msg)
    time.sleep(1)
    return msg

if __name__ == "__main__":
pool = Pool(processes = 3)
res_l=[]
for i in range(10):
    msg = "hello %d" %(i)
    res=pool.apply_async(func, (msg, ))   #维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去
    res_l.append(res)
print("==============================>") #没有后面的join,或get,则程序整体结束,进程池中的任务还没来得及全部执行完也都跟着主进程一起结束了

pool.close() #关闭进程池,防止进一步操作。如果所有操作持续挂起,它们将在工作进程终止前完成
pool.join()   #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束

print(res_l) #看到的是<multiprocessing.pool.ApplyResult object at 0x10357c4e0>对象组成的列表,而非最终的结果,但这一步是在join后执行的,证明结果已经计算完毕,剩下的事情就是调用每个对象下的get方法去获取结果
for i in res_l:
    print(i.get()) #使用get来获取apply_aync的结果,如果是apply,则没有get方法,因为apply是同步执行,立刻获取结果,也根本无需get

#二:使用进程池(阻塞,apply)
#coding: utf-8

from multiprocessing import Process,Pool 
import time

def func(msg):
    print( "msg:", msg)
    time.sleep(0.1)
    return msg

if __name__ == "__main__":
pool = Pool(processes = 3)
res_l=[]
for i in range(10):
    msg = "hello %d" %(i)
    res=pool.apply(func, (msg, ))   #维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去
    res_l.append(res) #同步执行,即执行完一个拿到结果,再去执行另外一个
print("==============================>")
pool.close()
pool.join()   #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束

print(res_l) #看到的就是最终的结果组成的列表
for i in res_l: #apply是同步的,所以直接得到结果,没有get()方法
    print(i)

使用进程池维护固定数目的进程

#Pool内的进程数默认是cpu核数,假设为4(查看方法os.cpu_count())
#开启6个客户端,会发现2个客户端处于等待状态
#在每个进程内查看pid,会发现pid使用为4个,即多个客户端公用4个进程

from socket import *
from multiprocessing import Pool
import os

server=socket(AF_INET,SOCK_STREAM)
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
server.bind(('127.0.0.1',8080))
server.listen(5)

def talk(conn,client_addr):
    print('进程pid: %s' %os.getpid())
  while True:
    try:
        msg=conn.recv(1024)
        if not msg:break
        conn.send(msg.upper())
    except Exception:
        break

if __name__ == '__main__':
p=Pool()
while True:
    conn,client_addr=server.accept()
    p.apply_async(talk,args=(conn,client_addr))
    # p.apply(talk,args=(conn,client_addr)) #同步的话,则同一时间只有一个客户端能访问

server端

from socket import *

client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))


while True:
   msg=input('>>: ').strip()
   if not msg:continue

client.send(msg.encode('utf-8'))
msg=client.recv(1024)
print(msg.decode('utf-8'))

回调函数(apply_async的扩展用法)
不需要回调函数的场景:如果在主进程中等待进程池中所有任务都执行完毕后,再统一处理结果,则无需回调函数

from multiprocessing import Pool
import time,random,os

def work(n):
   time.sleep(1)
   return n**2

if __name__ == '__main__':
p=Pool()

res_l=[]
for i in range(10):
    res=p.apply_async(work,args=(i,))
    res_l.append(res)

p.close()
p.join() #等待进程池中所有进程执行完毕

nums=[]
for res in res_l:
    nums.append(res.get()) #拿到所有结果
print(nums) #主进程拿到所有的处理结果,可以在主进程中进行统一进行处理

需要回调函数的场景:进程池中任何一个任务一旦处理完了,就立即告知主进程:我好了额,你可以处理我的结果了。主进程则调用一个函数去处理该结果,该函数即回调函数
我们可以把耗时间(阻塞)的任务放到进程池中,然后指定回调函数(主进程负责执行),这样主进程在执行回调函数时就省去了I/O的过程,直接拿到的是任务的结果。

from multiprocessing import Pool
import time,random,os

def get_page(url):
    print('(进程 %s) 正在下载页面 %s' %(os.getpid(),url))
    time.sleep(random.randint(1,3))
    return url #用url充当下载后的结果

def parse_page(page_content):
    print('<进程 %s> 正在解析页面: %s' %(os.getpid(),page_content))
    time.sleep(1)
    return '{%s 回调函数处理结果:%s}' %(os.getpid(),page_content)


if __name__ == '__main__':
urls=[
    'http://maoyan.com/board/1',
    'http://maoyan.com/board/2',
    'http://maoyan.com/board/3',
    'http://maoyan.com/board/4',
    'http://maoyan.com/board/5',
    'http://maoyan.com/board/7',

]
p=Pool()
res_l=[]

#异步的方式提交任务,然后把任务的结果交给callback处理
#注意:会专门开启一个进程来处理callback指定的任务(单独的一个进程,而且只有一个)
for url in urls:
    res=p.apply_async(get_page,args=(url,),callback=parse_page)
    res_l.append(res)

#异步提交完任务后,主进程先关闭p(必须先关闭),然后再用p.join()等待所有任务结束(包括callback)
p.close()
p.join()
print('{主进程 %s}' %os.getpid())

#收集结果,发现收集的是get_page的结果
#所以需要注意了:
#1. 当我们想要在将get_page的结果传给parse_page处理,那么就不需要i.get(),通过指定callback,就可以将i.get()的结果传给callback执行的任务
#2. 当我们想要在主进程中处理get_page的结果,那就需要使用i.get()获取后,再进一步处理
for i in res_l: #本例中,下面这两步是多余的
    callback_res=i.get()
    print(callback_res)

‘’’
打印结果:
(进程 52346) 正在下载页面 http://maoyan.com/board/1
(进程 52347) 正在下载页面 http://maoyan.com/board/2
(进程 52348) 正在下载页面 http://maoyan.com/board/3
(进程 52349) 正在下载页面 http://maoyan.com/board/4
(进程 52348) 正在下载页面 http://maoyan.com/board/5
<进程 52345> 正在解析页面: http://maoyan.com/board/3
(进程 52346) 正在下载页面 http://maoyan.com/board/7
<进程 52345> 正在解析页面: http://maoyan.com/board/1
<进程 52345> 正在解析页面: http://maoyan.com/board/2
<进程 52345> 正在解析页面: http://maoyan.com/board/4
<进程 52345> 正在解析页面: http://maoyan.com/board/5
<进程 52345> 正在解析页面: http://maoyan.com/board/7
{主进程 52345}
http://maoyan.com/board/1
http://maoyan.com/board/2
http://maoyan.com/board/3
http://maoyan.com/board/4
http://maoyan.com/board/5
http://maoyan.com/board/7
‘’’

from multiprocessing import Pool
import time,random
import requests
import re

def get_page(url,pattern):
   response=requests.get(url)
  if response.status_code == 200:
     return (response.text,pattern)

def parse_page(info):
   page_content,pattern=info
   res=re.findall(pattern,page_content)
  for item in res:
     dic={
        'index':item[0],
        'title':item[1],
        'actor':item[2].strip()[3:],
        'time':item[3][5:],
        'score':item[4]+item[5]

    }
    print(dic)

if __name__ == '__main__':
pattern1=re.compile(r'<dd>.*?board-index.*?>(\d+)<.*?title="(.*?)".*?star.*?>(.*?)<.*?releasetime.*?>(.*?)<.*?integer.*?>(.*?)<.*?fraction.*?>(.*?)<',re.S)

url_dic={
    'http://maoyan.com/board/7':pattern1,
}

p=Pool()
res_l=[]
for url,pattern in url_dic.items():
    res=p.apply_async(get_page,args=(url,pattern),callback=parse_page)
    res_l.append(res)

for i in res_l:
    i.get()

# res=requests.get('http://maoyan.com/board/7')
# print(re.findall(pattern,res.text))





















import multiprocessing,time
def run(name):
    time.sleep(5)
    print("来了",name)
    if __name__ == '__main__':
   
   p1 = multiprocessing.Process(target=run,kwargs={"name":"老弟1"},name="l1")
   p2 = multiprocessing.Process(target=run,kwargs={"name":"老弟2"},name="l2")
   p3 = multiprocessing.Process(target=run,kwargs={"name":"老弟3"},name="l3")
   #p1.daemon = True
  
   p1.start()
   p2.start()
   p3.start()
  #p1.terminate()
  #print(p1.is_alive())
  #time.sleep(6)
  #print(p1.is_alive())
  #p1.join(timeout=2)
   p1.terminate()
   print(p1.is_alive())
   time.sleep(1)
   print(p1.is_alive())
  #print("主线程结束")

‘’’

class MyProcess(multiprocessing.Process):
def __init__(self):
    multiprocessing.Process.__init__(self)
def run(self):
    print("来了,老弟")

if __name__=="__main__":
p1 = MyProcess()
p2 = MyProcess()
p1.start()
p2.start()

‘’’
‘’’

import multiprocessing,time

#list = [i for i in range(100)]

def work(name,lock):
lock.acquire()
with open("b.txt",mode="r+",encoding="utf-8") as f:
    c = int(f.read())
    c -= 1
    time.sleep(1)
    print(name,c)
    f.write(str(c))
lock.release()

if __name__ == '__main__':
lock = multiprocessing.Lock()

p1 = multiprocessing.Process(target=work,args=("p1",lock))
p2 = multiprocessing.Process(target=work,args=("p2",lock))
p3 = multiprocessing.Process(target=work,args=("p3",lock) )
p1.start()
p2.start()
p3.start()

‘’’
‘’’

import multiprocessing,time

def work(q):
if not q.empty():
    print(q.get())

if __name__ == '__main__':
q = multiprocessing.Queue(maxsize=3)
q.put(1)
q.put(2)
q.put(3)

p1 = multiprocessing.Process(target=work,args=(q,))
p2 = multiprocessing.Process(target=work,args=(q,))

p1.start()
p2.start()

‘’’
‘’’

import multiprocessing
#接收方=======================发送方
def jieshou(left,right):
  #right.close()
   while True:
      print(left.recv())
      left.send("你好")

def fasong(left,right):
#left.close()
 while True:
    right.send("我是被发送出去的数据")
    print(right.recv())

if __name__ == '__main__':
left,right = multiprocessing.Pipe()
p1 = multiprocessing.Process(target=jieshou,args=(left,right))
p2 = multiprocessing.Process(target=fasong,args=(left,right))
p1.start()
p2.start()

‘’’

import os
print(os.cpu_count())
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值