python基础 19 进程和线程②

201 阅读5分钟

多进程模块

1. 用于创建进程,替换了threading模块(将threading中的方法名移植过来) 
2. CPython下多线程是由GIL管理,导致多核模式下效率降低,GIL在进程之中的(不同的进程拥有不同的GIL),进 程就不会受到GIL的影响,多核模式下CPU密集型程序可以使用多进程 

一 multiprocessing

1. 主要用于多进程管理 
2. 该模块中的方法和多线程管理模块类似 
3. 包含Lock等类似于线程中的类 
4. 包含特殊类:Pipe,Queue进程通信相关的类(IPC) 

二 Process

Process:进程类 
1. Process.__init__(self, group=None, target=None, name=None, args=(), kwargs={}, daemon=None) 
    self:指代当前进程对象 
    group:预留参数 
    target:目标 
    name:进程名称 
    args:目标的参数(必须是元组) 
    kwargs:目标的参数(必须是字典) 
    daemon:是否是守护进程 
2. terminate() 结束当前进程 

import multiprocessing,time 
def music(name,n): 
    for i in range(n): 
        print('listen to the music %s %s times'%(name,i+1)) 
        time.sleep(0.5) 
        
def movie(name,n): 
    for i in range(n): 
        print('watch movie %s %s times'%(name,i+1))
        time.sleep(0.5) 
if __name__ == '__main__': 
p1=multiprocessing.Process(target=music,args=('凉凉',3)) 
p2=multiprocessing.Process(target=movie,args=('我不是药神',3))
p1.start()
p2.start() 
p1.join() 
p2.join() 
print('end') 

三 多进程模块的其他类

1. Lock:同步锁 
2. Pool:进程池 
3. Queue:多进程安全队列 
4. Pipe:管道 

四 Lock

同步和异步: 
1. 同步:原子操作不被破坏,多个进程同一时间只能有一个进程可以访问临界资源(或原子操作) 
2. 异步:多个进程同一只时间可以同时访问临界资源(或原子操作) 

进程之间相互独立,互不干扰 
# 进程同步 
import multiprocessing as m,time 
lock=m.Lock() 

class MyList(list): 
    def __init__(self): 
        self.l=['A','B','','',''] 
        self.index=2 
        
    def add(self,value): l
        ock.acquire() 
        self.l[self.index]=value 
        time.sleep(0.001) 
        self.index+=1 
        lock.release() 
        
    def getList(self): 
        return self.l 
        
mylist=MyList() 
    def func(char): 
        mylist.add(char) 
        
if __name__ == '__main__': 
p1=m.Process(target=func,args=('C',)) 
p2=m.Process(target=func,args=('D',)) 
p1.start() 
p2.start() 
p1.join() 
p2.join() 
print(mylist.getList()) # 进程间各自独立,所以子进程修改全局变量不会影响当前主进程 

五 Pool

Pool:进程池 
1. 进程池可以提供指定数量的进程,用于调用 
    1. 当有新的进程请求提交到Pool中是,如果池没满,就会创建一个新的进程 
    2. 如果满了,则新的进程的请求就会等待 
    3. 如果Pool结束,则不会创建任何进程,也不会处理任何请求 
2. 进程池的方法: 
    1. apply(func,[args,[kwargs]]): 
        阻塞执行,进程池中的进程,每次只能执行一个 
        Python3弃用该方法 
    2. apply_async(func,[args,[kwargs,[callback]]]) 
        非阻塞执行,进程池中的进程,异步执行 
    3. close(): 
        关闭pool,不再接收新的任务,池中的任务继续执行 
    4. terminate(): 
        结束工作进程,不再处理未完成的进程任务 
    5. join(): 
        阻塞当前进程,等待子进程的退出(join必须在close或terminate之后使用) 
3. 进程池批量创建进程 
import multiprocessing as m,time 
def work(n):
    print('Hello world: %s'%n) 
    time.sleep(1) 
if __name__ == '__main__': 
pool=m.Pool(3) # 设置进程池的容量 
for i in range(10): 
    pool.apply_async(func=work,args=(i,)) 
pool.close() 
pool.join() # 必须调用:如果主进程执行完毕,进程池还没执行完,主进程结束,控制台不再接收打印任务 

六 Queue

1. Queue:队列 进程安全队列 
2. 进程之间是相互独立的,互不干扰 
    让进程之间可以进行通信的一种数据结构 
3. 队列的创建 
    Queue([maxsize])
    maxsize:设置队列的容量,不设置表示无限大(不限制大小)
4. 队列对象的方法: 
1. put(value): 向队列中传入值 blocked参数:bool类型 timeout参数:超时时长 如果blocked为True,并且timeout为正值,如果队列已满,到达超时时间则抛出异常: Queue.Full异常;如果为blocked为False如果队列已满,直接抛出异常 
2. get():从队列中读取,并删除一个元素 blocked参数:bool类型 timeout参数:超时时长 如果blocked为True,并且timeout为正值,如果队列已空,到达超时时间则抛出异常: Queue.Full异常;如果为blocked为False如果队列已空,直接抛出异常 
3. get_nowait():get(False) 
4. put_nowait():put(False)
5. empty(): 判断队列是否为空,该方法不可靠 
6. full(): 判断队列是否满了,该方法不可靠 
7. qsize(): 返回队列中的元素个数,该方法不可靠

import multiprocessing as m ,time 
def putValues(q): # 向一个队列中插入数据 q:队列对象
    for i in ['A','B','C']: # i:A B C 
        print('发送%s到queue'%i) 
        q.put(i) 
        time.sleep(1) 
        
def getValues(q):
    while True: 
        value=q.get() 
        print('从queue中获取到数据%s'%value) 
        
if __name__ == '__main__': 
queue=m.Queue() # 创建队列对象 
process1=m.Process(target=putValues,args=(queue,)) # 向队列中放入数据 
process2=m.Process(target=getValues,args=(queue,)) # 从队列取出数据 
process1.start() 
process2.start()
process1.join() 
process2.terminate() 
print('end') 

七 Pipe

Pipe:管道 
1. 用于进程之间的通信 Pipe的底层实现是队列 
2. Pipe(duplex): 
    duplex:双向的,通过的,设置管道对象的运行模式 
    返回一个元组,(conn1,conn2)代表了管道的两端
    duplex:True:全双工模式(conn1和conn2都可以接收也可以发送) Flase:半双工模式(conn1只负责接收,conn2只负责发送) 
3. send(mesg): 管道两端的方法 mesg:消息(对象,元素) 发送消息 
4. recv(): 管道两端的方法 
    接收消息 
    接收消息提前做好准备:消息接收会让处于阻塞状态,用于等待消息的到来 
    如果管道关闭,调用recv方法,抛出EOFError(异常终止) 

import multiprocessing as m,time
def putValues(p): # p此时为管道对象 
    for i in ['A','B','C']: 
        print('发送%s到pipe' % i) 
        p[1].send(i) 
        time.sleep(1) 

def getValues(p): 
    while 1: 
        value=p[0].recv() 
        print('从pipe中获取到数据%s'%value) 
        
if __name__ == '__main__': 
# 创建管道对象 pipe=m.Pipe(duplex=False) 
process_in=m.Process(target=putValues,args=(pipe,)) 
process_out=m.Process(target=getValues,args=(pipe,)) 
process_in.start() 
process_out.start() 
process_in.join() 
process_out.terminate() 
print('end') 

八 生产者消费者模型

对于一块内存空间,有任务向该空间存储数据,同时也会有任务从该空间获取数据---生产者消费者模型(业务模型) 
 队列: 
 1. 跨进程通信队列 
     multiprocessing.Queue 
     用于进程通信 
 2. 队列模块 
     queue.Queue 
     不属于线程 
     
 import threading,time,queue 
 queue=queue.Queue() 
 
 def producer(name):# 生产者 
     count=1 
     while 1: 
         queue.put('第%s根香烟'%count) 
         print('%s 生产了第%s根香烟'%(name,count)) 
         count+=1 
         time.sleep(1) 
 
 def consumer(name): # 消费者
     while 1: 
         print('%s 拿到了 %s'%(name,queue.get())) 
         time.sleep(2) 
 
 if __name__ == '__main__': 
 l=[] 
 l.append(threading.Thread(target=producer,args=('老王',))) 
 l.append(threading.Thread(target=consumer,args=('张三',))) 
 l.append(threading.Thread(target=consumer,args=('李四',))) 
 for i in l: 
     i.start()