• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • YouClaw
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
KK筑梦人
博客园    首页    新随笔    联系   管理    订阅  订阅

上节复习、进程间通信(Piep/管道)、进程间的数据共享(Manager)、进程池(Pool/效率,返回值,回调函数) 第三十三天 2018.11.22

上节复习:

进程同步控制:

信号量 Semaphore

from multiprocessing import Semaphore

用锁的原理实现的,内置了一个计数器

在同一时间 只能有指定数量的进程执行某一段被控制住的代码

 

事件 Event

wait阻塞收到事件状态控制的同步组件

状态 True False is_set

    true -> false clear()

    false -> true set()

wait 状态为True不阻塞 状态为False的时候阻塞

 

锁、信号量、事件 ----> 同步控制器

 

进程间通信:

队列 Queue

    put 当队列满的时候阻塞等待队列有空位置

    get 当队列空的时候阻塞等待队列有数据

    full empty 不完全准确 (可能在数据返回另一个子进程时间,队列被另一个子进程取值)

JoinableQueue

    get <----> task_done

    put <----> join

进程间通信---->Pipe管道:

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

 管道使用注意事项:

  1.建立管道对象必须在产生Process对象之前

       2.管道两端连接对象---->一端为发送端(send)、一端为接收端(recv)

  3.需要在子进程中关闭子进程中的两端管道,子进程代码才会执行完毕

  4.通过捕捉EOFError错误关闭管道接收端时---->需要把管道中的除了该进程出口外的进口出口全部关闭

from multiprocessing import Pipe,Process
                 # 导入管道---->注意管道必须在产生进程对象前产生
def func(conn1,conn2):
    conn2.close()     # 因为conn2为管道发送端---->此子进程不需要向主进程发送数据,所以不需要的关闭
    while True:
        try :      # 当主进程发送完数据时间,msg会阻塞,当出了子进程接收数据的所有出口都关闭时间
     #  子进程接收不到数据会报EOFError错误,捕获错误关闭子进程接收端管道从而使子进程代码执行完毕
            msg = conn1.recv()  # 阻塞---->当管道内没数据时间会阻塞
            print(msg)
        except EOFError:     # 当管道入口都被关闭,管道出口只剩一个时间才会报错才能捕捉异常
            conn1.close()
            break              # 退出循环,子进程代码执行完毕

if __name__ == '__main__':
    conn1, conn2 = Pipe()        # 管道两端的连接对象      此模块中conn1为接收端,conn2为发送端
    Process(target=func,args = (conn1,conn2)).start()    # 开启一个子进程对象
    conn1.close()        # 在主进程中关闭conn1,不影响子进程中的conn1
    for i in range(20):
        conn2.send('吃了么')   # 主进程向子进程发送数据
    conn2.close()              # 主进程发送完数据后关闭掉主进程发送端

管道实现生产者消费者模型(Lock控制管道间通信): 

# 通过锁来控制管道间通信
from multiprocessing import Lock,Pipe,Process
# 导入锁、管道、进程
def producer(con,pro,name,food):   # 生产者  
    con.close()                # 关闭生产者的入口---->生产者此模块不需要接收
    for i in range(100):
        f = '%s生产%s%s'%(name,food,i)    # 谁生产了什么第几个 
        print(f)
        pro.send(f)            # 把生产者东西通过管道发送出去
    pro.send(None)
    pro.send(None)
    pro.send(None)            # 因为有三个消费者,所以需要send3个None来结束3个消费者的管道入口
    pro.close()               # 生产完毕关闭生产者的管道出口

def consumer(con,pro,name,lock):   # 消费者
    pro.close()        # 消费者接收东西,不需要出口,所以关闭
    while True:
            lock.acquire()       # 拿钥匙
            food = con.recv()    # 某个消费者拿到某个东西
            lock.release()       # 还钥匙
            if food is None:  # 通过判断接收的是否为None,依次关闭三个消费者的管道入口
                con.close()
                break
            print('%s吃了%s' % (name, food))  # 不能消费者吃了None因此在break后
if __name__ == '__main__':
    con,pro = Pipe()    # 建立管道对象 con为管道入口、pro为管道出口
    lock= Lock()        # 在消费者端加锁,防止一个数据被多个消费者同时抢到
    p = Process(target=producer,args=(con,pro,'egon','包子'))     
    c1 = Process(target=consumer, args=(con, pro, 'alex',lock))
    c2 = Process(target=consumer, args=(con, pro, 'bossjin',lock))
    c3 = Process(target=consumer, args=(con, pro, 'wusir',lock))
    c1.start()
    c2.start()
    c3.start()
    p.start()
    con.close()        # 主进程关闭管道入口
    pro.close()        # 主进程关闭管道出口

pipe 数据不安全性 ----> 多个消费者取一个数据 ----> 加锁

IPC(Inter-Process Communication)

加锁来控制操作管道的行为 来避免进程之间争抢数据造成的数据不安全现象

队列 进程之间数据安全的

队列 == 管道(需要每次关闭不用使用的) + 锁

进程间的数据共享(Manager):     

进程间抢占资源造成的数据不安全问题

单个展示:

from multiprocessing import Manager,Process
# 进程间抢占资源造成的数据不安全问题
def main(dic):
    dic['count'] -= 1
    print(dic)

if __name__ == '__main__':
    m = Manager()
    dic=m.dict({'count':100})
    p_lst = []
    p = Process(target=main, args=(dic,))
    p.start()
    p.join()

多个展示(不加锁会造成数据错乱):

from multiprocessing import Manager,Process,Lock
def main(dic,lock):
    dic['count'] -= 1

if __name__ == '__main__':
    m = Manager()                 # 自动实现了锁的两种方法---->l.acquire() 、l.release()
    l = Lock()
    dic=m.dict({'count':100})    # dict为Manger对象的内置方法,可以直接操作
    p_lst = []
    for i in range(50):          # 50个子进程在锁的作用下依次对dic进行操作
        p = Process(target=main,args=(dic,l))
        p.start()
        p_lst.append(p)
    for i in p_lst: i.join()     # 等待50个子进程依次结束
    print('主进程',dic)

进程池:

为什么会有进程池的概念

  效率

  每开启进程,开启属于这个进程的内存空间

  寄存器 堆栈 文件

  进程过多 操作系统的调度

进程池   个数一般情况下为cpu个数+1

  python中的 先创建一个属于进程的池子

  这个池子指定能存放n个进程

  先讲这些进程创建好

  超过5个进程用进程池

更高级的进程池   Python中没有这个,其他语言中有

  n,m n下限、m上限

  3 三个进程

     + 进程

  20 20个

import time
from multiprocessing import Pool,Process
def func(n):
    for i in range(10):         # 每个进程在此循环10次
        print(n+1)

if __name__ == '__main__':
    start = time.time()
    pool = Pool(5)               # 5个进程
    pool.map(func,range(100))    # 100个任务每次去5个任务到进程池  必须为可迭代
    t1 = time.time() - start     # 0.3280487060546875

    start = time.time()
    p_lst = []
    for i in range(100):
        p = Process(target=func,args=(i,))
        p_lst.append(p)
        p.start()
    for p in p_lst :p.join()
    t2 = time.time() - start     # 5.4934046268463135
    print(t1,t2)

利用os模块直观:

import os     # 通过os模块中的方法看到进程号更直观
import time
from multiprocessing import Pool
def func(n):
    print('start func%s'%n,os.getpid())
    time.sleep(1)
    print('end func%s' % n,os.getpid())

# 第一次有5个任务进入进程池---->之后每结束一个任务,就有一个任务进入进程池
if __name__ == '__main__':
    p = Pool(5)            # 可以不带参数,默认为CPU数
    for i in range(10):
        # apply同步
        p.apply_async(func,args=(i,))   # 异步主进程结束后不等待子进程
    p.close()  # 结束进程池接收任务
    p.join()   # 感知进程池中的任务执行结束

进程池server_client

server端:

import socket
from multiprocessing import Pool

def func(conn):
    conn.send(b'hello')
    print(conn.recv(1024).decode('utf-8'))
    conn.close()

if __name__ == '__main__':
    p = Pool(5)                              # 进程池设置为5个进程
    sk = socket.socket()
    sk.bind(('127.0.0.1',8080))
    sk.listen()          # server端只需要建立一次套接字对象、绑定机器端口、监听
    while True:
        conn, addr = sk.accept()             # 建立连接
        p.apply_async(func,args=(conn,))     # 建立异步连接对象最多同时5个,每执行完一个可以再有一个
    sk.close()

client端:  

import socket

sk = socket.socket()
sk.connect(('127.0.0.1',8080))

ret = sk.recv(1024).decode('utf-8')
print(ret)
msg = input('>>>').encode('utf-8')
sk.send(msg)
sk.close()

进程池中的方法:

  p.map(funcname,iterable) 默认异步的执行任务,且自带close和join

  p.apply 同步调用的

  p.apply_async 异步调用 和主进程完全异步 需要手动close 和 join

进程池中的返回值:

同步:

from multiprocessing import Pool
def func(i):
    return i*i

if __name__ == '__main__':
    p = Pool(5)
    for i in range(10):
        res = p.apply(func,args=(i,))   # apply的结果就是func的返回值
        print(res)

异步:  

import time
from multiprocessing import Pool
def func(i):
    time.sleep(0.5)
    return i*i

if __name__ == '__main__':
    p = Pool(5)
    res_l = []
    for i in range(10):
        res = p.apply_async(func,args=(i,))   # apply的结果就是func的返回值
        res_l.append(res)
    for res in res_l:print(res.get())# 等着 func的计算结果

map方法:  

import time
from multiprocessing import Pool
def func(i):
    time.sleep(0.5)
    return i*i

if __name__ == '__main__':
    p = Pool(5)
    ret = p.map(func,range(100))   # 把所有的执行完后,统一放入ret中
                                   # map自带close和join
    print(ret)

进程池的回调函数: 

 

# 回调函数
import os
from multiprocessing import Pool
def func1(n):
    print('in func1',os.getpid())
    return n**2

def func2(nn):                # 回调函数在主进程执行    回主进程,再调用func2
    print('in func2',os.getpid())
    print(nn)                 # 回调函数的参数为func1的返回值,,10时nn为10,  1000时nn为1000

if __name__ == '__main__':
    print('主进程 :',os.getpid())
    p = Pool(5)
    for i in range(10):
        p.apply_async(func1,args=(10,),callback=func2)   # callback = func2回调func2函数
    p.close()
    p.join()

  

 

posted @ 2018-11-22 22:16  KK筑梦人  阅读(176)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3