1.管道
from multiprocessing import Process,Pipe def consumer(p,name): produce, consume=p produce.close() while True: try: baozi=consume.recv() print('%s 收到包子:%s' %(name,baozi)) except EOFError: break def producer(seq,p): produce, consume=p consume.close() for i in seq: produce.send(i) if __name__ == '__main__': produce,consume=Pipe() c1=Process(target=consumer,args=((produce,consume),'c1')) c1.start() seq=(i for i in range(10)) producer(seq,(produce,consume)) produce.close() consume.close() c1.join() print('主进程')
from multiprocessing import Process,Pipe,Lock def consumer(p,name,lock): produce, consume=p produce.close() while True: lock.acquire() baozi=consume.recv() lock.release() if baozi: print('%s 收到包子:%s' %(name,baozi)) else: consume.close() break def producer(p,n): produce, consume=p consume.close() for i in range(n): produce.send(i) produce.send(None) produce.send(None) produce.close() if __name__ == '__main__': produce,consume=Pipe() lock = Lock() c1=Process(target=consumer,args=((produce,consume),'c1',lock)) c2=Process(target=consumer,args=((produce,consume),'c2',lock)) p1=Process(target=producer,args=((produce,consume),10)) c1.start() c2.start() p1.start() produce.close() consume.close() c1.join() c2.join() p1.join() print('主进程')
# pipe 数据不安全性
# IPC
# 加锁来控制操作管道的行为 来避免进程之间争抢数据造成的数据不安全现象
# 队列 进程之间数据安全的
# 管道 + 锁
2.进程之间的数据共享
展望未来,基于消息传递的并发编程是大势所趋
即便是使用线程,推荐做法也是将程序设计为大量独立的线程集合,通过消息队列交换数据。
这样极大地减少了对使用锁定和其他同步手段的需求,还可以扩展到分布式系统中。
但进程间应该尽量避免通信,即便需要通信,也应该选择进程安全的工具来避免加锁带来的问题。
以后我们会尝试使用数据库来解决现在进程之间的数据共享问题。
进程间数据是独立的,可以借助于队列或管道实现通信,二者都是基于消息传递的 虽然进程间数据独立,但可以通过Manager实现数据共享,事实上Manager的功能远不止于此 A manager object returned by Manager() controls a server process which holds Python objects and allows other processes to manipulate them using proxies. A manager returned by Manager() will support types list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value and Array.
# 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 = Lock() dic=m.dict({'count':100}) p_lst = [] for i in range(50): p = Process(target=main,args=(dic,l)) p.start() p_lst.append(p) for i in p_lst: i.join() print('主进程',dic)
3.进程池和multiprocess.Pool模块
进程池
为什么要有进程池?进程池的概念。
在程序实际处理问题过程中,忙时会有成千上万的任务需要被执行,闲时可能只有零星任务。那么在成千上万个任务需要被执行的时候,我们就需要去创建成千上万个进程么?首先,创建进程需要消耗时间,销毁进程也需要消耗时间。第二即便开启了成千上万的进程,操作系统也不能让他们同时执行,这样反而会影响程序的效率。因此我们不能无限制的根据任务开启或者结束进程。那么我们要怎么做呢?
在这里,要给大家介绍一个进程池的概念,定义一个池子,在里面放上固定数量的进程,有需求来了,就拿一个池中的进程来处理任务,等到处理完毕,进程并不关闭,而是将进程再放回进程池中继续等待任务。如果有很多任务需要执行,池中的进程数量不够,任务就要等待之前的进程执行任务完毕归来,拿到空闲进程才能继续执行。也就是说,池中进程的数量是固定的,那么同一时间最多有固定数量的进程在运行。这样不会增加操作系统的调度难度,还节省了开闭进程的时间,也一定程度上能够实现并发效果。
import time from multiprocessing import Pool,Process def func(n): for i in range(10): print(n+1) def func2(n): for i in range(10): print(n) if __name__ == '__main__': start = time.time() pool = Pool(5) # 5个进程 pool.map(func,range(100)) # 100个任务 pool.map(func2,range(100)) # 100个任务 # pool.map(func2,[('alex',1),'egon']) # 100个任务 t1 = time.time() - start print(t1) # 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 # print(t2) # print(t1,t2)
# 为什么会有进程池的概念 # 效率 # 每开启进程,开启属于这个进程的内存空间 # 寄存器 堆栈 文件 # 进程过多 操作系统的调度 # 进程池 # python中的 先创建一个属于进程的池子 # 这个池子指定能存放n个进程 # 先讲这些进程创建好 # 更高级的进程池 # n,m # 3 三个进程 # + 进程 # 20 20个 import time from multiprocessing import Pool,Process def func(n): for i in range(10): print(n+1) def func2(n): for i in range(10): print(n+2) if __name__ == '__main__': start = time.time() pool = Pool(5) # 5个进程 pool.map(func,range(100)) # 100个任务 pool.map(func2,[('alex',1),'egon']) # 100个任务 t1 = time.time() - start 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 print(t1,t2)
import os import time from multiprocessing import Pool def func(n): print('start%s'%n,os.getpid()) time.sleep(1) print('end%s' % n, os.getpid()) if __name__ =='__main__': p = Pool(5) for i in range(10): p.apply(func,args = (i,)) # p.apply_async(func,args = (i,)) # p.close() # p.join()
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) sk = socket.socket() sk.bind(('127.0.0.1',8080)) sk.listen() while True: conn,recv = sk.accept() p.apply_async(func, args=(conn,)) sk.close()
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()
回调函数
需要回调函数的场景:进程池中任何一个任务一旦处理完了,就立即告知主进程:我好了额,你可以处理我的结果了。主进程则调用一个函数去处理该结果,该函数即回调函数 我们可以把耗时间(阻塞)的任务放到进程池中,然后指定回调函数(主进程负责执行),这样主进程在执行回调函数时就省去了I/O的过程,直接拿到的是任务的结果。
# 回调函数是在主进程中执行的 from multiprocessing import Pool def func1(n): return n+1 def func2(m): print(m) if __name__ == '__main__': p = Pool(5) for i in range(10,20): p.apply_async(func1,args=(i,),callback=func2) p.close() p.join()
import requests # from urllib.request import urlopen from multiprocessing import Pool # 200 网页正常的返回 # 404 网页找不到 # 502 504 def get(url): response = requests.get(url) if response.status_code == 200: return url,response.content.decode('utf-8') # def get_urllib(url): # ret = urlopen(url) # return url,ret.read().decode('utf-8') def call_back(args): print(args) url,content = args print(url,len(content)) if __name__ == '__main__': url_lst = [ 'https://www.cnblogs.com/', 'http://www.baidu.com', 'https://www.sogou.com/', 'http://www.sohu.com/', ] p = Pool(5) for url in url_lst: # p.apply_async(get_urllib,args=(url,),callback=call_back) p.apply_async(get,args=(url,),callback=call_back) p.close() p.join()
import re from urllib.request import urlopen from multiprocessing import Pool def get_page(url,pattern): response=urlopen(url).read().decode('utf-8') return pattern,response def parse_page(info): pattern,page_content=info res=re.findall(pattern,page_content) for item in res: dic={ 'index':item[0].strip(), 'title':item[1].strip(), 'actor':item[2].strip(), 'time':item[3].strip(), } print(dic) if __name__ == '__main__': regex = r'<dd>.*?<.*?class="board-index.*?>(\d+)</i>.*?title="(.*?)".*?class="movie-item-info".*?<p class="star">(.*?)</p>.*?<p class="releasetime">(.*?)</p>' pattern1=re.compile(regex,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()
4.线程概念的引入背景
import os from threading import Thread import time def func(a,b): n = a + b print(n, os.getpid()) time.sleep(1) print(n) print('zhu',os.getpid()) for i in range(10): t = Thread(target = func,args = (i,10)) t.start() # # class MyTread(Thread): # def __init__(self,arg): # super().__init__() # self.arg = arg # def run(self): # time.sleep(1) # print(self.arg) # # t = MyTread(100) # t.start()
import os import time from threading import Thread # 多线程并发 # def func(a,b): # global g # g = 0 # print(g,os.getpid()) # # g = 100 # t_lst = [] # for i in range(10): # t = Thread(target=func,args=(i,5)) # t.start() # t_lst.append(t) # for t in t_lst : t.join() # print(g)
# 进程 是 最小的 内存分配单位 # 线程 是 操作系统调度的最小单位 # 线程直接被CPU执行,进程内至少含有一个线程,也可以开启多个线程 # 开启一个线程所需要的时间要远远小于开启一个进程 # 多个线程内部有自己的数据栈,数据不共享 # 全局变量在多个线程之间是共享的 # GIL锁(即全局解释器锁) # 在Cpython解释器下的python程序 在同一时刻 多个线程中只能有一个线程被CPU执行 # 高CPU : 计算类 --- 高CPU利用率 # 高IO : 爬取网页 200个网页 # qq聊天 send recv # 处理日志文件 读文件 # 处理web请求 # 读数据库 写数据库 import time from threading import Thread from multiprocessing import Process def func(n): n + 1 if __name__ == '__main__': start = time.time() t_lst = [] for i in range(100): t = Thread(target=func,args=(i,)) t.start() t_lst.append(t) for t in t_lst:t.join() t1 = time.time() - start start = time.time() t_lst = [] for i in range(100): t = Process(target=func, args=(i,)) t.start() t_lst.append(t) for t in t_lst: t.join() t2 = time.time() - start print(t1,t2)
import socket from threading import Thread def func(conn): conn.send(b'hello') msg = conn.recv(1024).decode('utf-8') print(msg) conn.close() sk = socket.socket() sk.bind(('127.0.0.1',8080)) sk.listen() while True: conn,addr = sk.accept() Thread(target=func,args = (conn,)).start() sk.close()
import socket from threading import Thread sk = socket.socket() sk.connect((('127.0.0.1'),8080)) msg = sk.recv(1024).decode('utf-8') print(msg) inp = input('>>>').encode('utf-8') sk.send(inp) sk.close()
import time import threading def wahaha(n): time.sleep(0.5) print(n,threading.current_thread(),threading.get_ident()) for i in range(10): threading.Thread(target=wahaha,args=(i,)).start() print(threading.active_count()) # 10 print(threading.current_thread()) print(threading.enumerate())