Day34 python基础--并发编程基础3

一,进程之间通信,简称:IPC:inter-process-communication

  概念:

    1.多个进程之间有一些固定的通信内容(一些信号)

    2.实现方式:socket基于文件家族通信

    3.进程之间虽然内存不共享,但是可以通信的

      Lock,Semaphore,Event 都会进行进程之间的通信,只不过这些通信的内容我们不能改变

  

  进程队列:先进先出 ****

import queue
from multiprocessing import Queue
q = Queue()    #若不设置,则对列长度不限制
q = Queue(2)   #设置队列长度,队列满了会阻塞

#put()与put_nowait()
q.put()   #若队列已满,数据进不去,并阻塞
q.put_nowait(3) #若队列已满,进不去队列,则报错,并丢弃本次数据

#get()与get_nowait()
q.get()  #若队列为空,或取值不存在,则阻塞
q.get_nowait(3)   #如果get队列,队列为空,或取值不存在,则报错

#empty()与full()  慎用,在多进程对一个队列操作时,不准!
q.empty():查看队列是否为空
q.full():查看队列是否为满


#nowait用法
try:
    print(q.get_nowait())  
except queue.Empty:
    print('empty')
#例:主进程与子进程通过进程队列传递数据
from mulitprocessing import Process,Queue
def consume(q):
    print(q.get())  #取值
    q.put('abc')    #存值
if __name__ == '__main__':
    q = Queue()
    p = Process(target=consume,args=(q,))
    p.start()
    q.put({'123':123})  #存值
    p.join()  #等待子进程执行完成
    print(q.get())   #取值

  

  进程队列应用:生产者消费者模型

#做饭吃饭的例子
import time
import random
from multiprocessing import Queue,Process
def consumer(q,name):  #处理数据/消费者
    while True:
        food = q.get()
        if food is None:break
        print('%s吃了一个%s'%(name,food))
        time.sleep(random.uniform(0.5,1))

def producer(q,name,food):  #获取数据/生成者
    for i in range(10):
        time.sleep(random.uniform(0.3,0.8))
        print('%s生产了%s%s'%(name,food,i))
        q.put(food+str(i))

if __name__ == '__main__':
    q = Queue()
    c1 = Process(target=consumer,args=(q,'alex'))
    c2 = Process(target=consumer,args=(q,'wusir'))
    # c1.daemon = True
    c1.start()
    c2.start()
    p1 = Process(target=producer,args=(q,'taibai',''))
    p2 = Process(target=producer,args=(q,'egon',''))
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    q.put(None)    #有几个consumer就需要在最后放几个none
    q.put(None)

 

  进程队列之joinablequeue模块:

    joinablequeue是一个类,包含put(),get(),task_done(),join()方法

import time
import random
from multiprocessing import Queue,Process
from multiprocessing import JoinableQueue
def consumer(q,name):  #处理数据
    while True:
        food = q.get()
        print('%s吃了一个%s'%(name,food))
        time.sleep(random.uniform(0.5,1))
        q.task_done()  #向q.join()发送一次信号,证明一个数据已经被取走了

def producer(q,name,food):  #获取数据
    for i in range(10):
        time.sleep(random.uniform(0.3,0.8))
        print('%s生成%s%s'%(name,food,i))
        q.put(food+str(i))  #生产数据并存值到进程队列

if __name__ == '__main__':
    q = JoinableQueue()
    c1 = Process(target=consumer,args=(q,'alex'))
    c1.daemon = True  #消费者进程设置为守护进程
    c2 = Process(target=consumer,args=(q,'wusir'))
    c2.daemon = True #消费者进程设置为守护进程
    c1.start()
    c2.start()
    p1 = Process(target=producer,args=(q,'taibai',''))
    p2 = Process(target=producer,args=(q,'egon',''))
    p1.start()
    p2.start()
    p1.join()  #生产者进程执行完成前阻塞
    p2.join()  #生产者进程执行完成前阻塞
    q.join()   #生产完毕后,使用此方法进行阻塞,直到队列中所有项目均被处理(即:ask_done的值为0)
    #当队列中的数据被处理完后,结束阻塞,消费者程序随着主进程代码完成而结束

 

  二,管道  **

    1.队列是基于管道实现

    2.管道是基于socket、pickle实现的

    3.管道与队列的区别

      队列:进程之间数据安全,严格控制数据进出(管道+锁实现简便的IPC机制)

      管道:进程之间数据是不安全的,且存取数据复制

    4.pipe的端口管理:

      pipe的端口管理不会随着某一个进程的关闭而关闭,pipe的端口属于系统资源,需要手动释放

      操作系统来管理进程对这个端口的使用

      操作系统管理pipe端口,每当手动关闭一个端口计算-1,直到所有可以传值的端口关闭,剩余一个端口的时候,系统报错,我们可以利用异常处理对报错信息进行处理并关闭子进程。

from multiprocessing import Pipe
left,right = Pipe()
left.send(12345)   #管道中的存值与取值,send,recv通过pickel和socket实现
print(right.recv())
from multiprocessing import Pipe,Process
def consumer(left,right):
    left.close()   #关闭子进程right端口
    while True:
        try:
            print(right.recv())  #当子进程取不到值时,并可以Pipe可以接受传值的端口都被关闭时,则报错
        except EOFError:  #利用报错,进行异常处理,以结束子进程
            break
if __name__ == '__main__':
    left, right = Pipe()
    Process(target=consumer, args=(left, right)).start()
    right.close()   #关闭主进程right端口
    for i in range(10):
        left.send('饭菜%s'%i)
    left.close()   #当数据存取完成是,关闭left端口,否则子程序的pipe端口管理会认为,数据还没有存取完毕,子进程会进入阻塞状态

  

  三,进程池  *****

    1:为什么要有进程池?

      开启过多的进程并不能提高你的效率,反而会降低效率

    2.进程任务的概念:

      计算密集型:充分占用cpu,多进程可以充分利用多核

        适合开启多进程,但是不适合开启很多多进程

      IO密集型:大部分时间都在阻塞队列,而不是在运行状态中

        根本不适合开启多进程

    3.进程池的创建

#实例化 传参数(进程的个数) 进程个数默认为cpu个数 或 为1,一般为os.cpu_count() + 1
p = Pool()
#提交任务
    #同步提交func到一个子进程中执行
        ret = p.apply(func,args=(i,))
        #返回值:子进程对应函数的返回值
        #一个一个顺序执行,并没有任何并发效果
    #异步提交func到一个子进程中执行       
        ret = p.apply(apply_async,args=(i,))
        #没有返回值,要想所有任务能够顺利的执行完毕
            p.close()   #关闭进程池,用户不能再向这个池中提交任务了
            p.join()   #必须先close再join,阻塞直到进程池中所有任务执行完毕
        #有返回值的情况下
            ret.get()#get不能在提交任何之后立刻执行,应该先提交所有任务,再通过get得到返回值
        #map()方法
            #异步提交的简化版本
            #自带close和join方法
#例:进程池与多进程的效率对比
import time
from multiprocessing import Pool,Process
def func(num):
    print('做了第%s件衣服'%num)

if __name__ == '__main__':
    #以进程池的形式执行500个任务
    strat = time.time()
    p = Pool(4)
    for i in range(500):
        p.apply_async(func,args=(i,))
    p.close()
    p.join()
    print(time.time() - strat)

    #以多进程的形式执行100个任务
    strat = time.time()
    p_lst = []
    for i in range(100):
        p = Process(target=func,args=(i,))
        p.start()
        p_lst.append(p)
    for p in p_lst:
        p.join()
    print(time.time() - strat)
#以完全同步的方式提交任务(apply)
import os
import time
from multiprocessing import Pool

def task(num):
    time.sleep(1)
    print('%s:%s'%(num,os.getpid()))
    return num**2  #进程池有IPC机制,可以直接return

if __name__ == '__main__':
    p = Pool(4)   
    for i in range(20):
        ret = p.apply(task,args=(i,))  #提交任务的方法,同步提交(自带join阻塞)
        print(ret)    #任务逐个提交并完成后再返回
#以完全异步的方式提交任务(apply_async)
import os
import time
from multiprocessing import Pool

def task(num):
    time.sleep(1)
    print('%s:%s'%(num,os.getpid()))
    return num**2
if __name__ == '__main__':
    p = Pool(4)   #默认值为os.cpu_count() or 1 ,一般为os.cpu_count()+1
    res_lst = []
    for i in range(20):
        ret = p.apply_async(task,args=(i,))
        res_lst.append(ret)
    for res in res_lst:  #异步方式取返回值
        print(res.get())

 

posted @ 2018-09-20 17:16  乘地铁  阅读(199)  评论(0编辑  收藏  举报