一,进程的数据共享机制--Manager模块
1.把所有实现了数据共享的比较便捷的类都重新封装了一遍
2.并且在原有的multiprocessing的基础上,增加了新的机制list、dict
3.支持的数据类型非常有限
4.list、dict都不是数据安全的,需要自行加锁来保证数据安全
5.工作中一般是通过第三方工具实现进程间的数据共享
from multiprocessing import Manager,Process,Lock def work(d,lock): with lock: # lock.acquire() d['count'] -= 1 #操作数据是一个取值赋值过程 # lock.release() if __name__ == '__main__': lock = Lock() m = Manager() #with Manager() as m: dic = m.dict({'count':100}) p_1 = [] for i in range(100): p = Process(target=work,args=(dic,lock)) p_1.append(p) p.start() for p in p_1: p.join() print(dic)
二,进程的回调函数
1.当子进程执行完毕之后执行callback函数
2.子进程的返回值会作为callback的参数
3.回调函数在主进程执行
应用场景:
1.使用主进程统一的获取结果,这样的化在进程之间没有数据隔离
2.多个子进程所做的事情是比较长时间的,而主进程中回调函数的执行速度都比较快
子进程中做网络访问,主进程处理网页的结果
子进程有大量的计算要去做,回调函数等待结果做简单处理
def func(i): print('第一个任务',os.getpid()) return '*'*i, def call_back(res): #只能接受一个参数,可以*res;本体是args*,传多个参数会聚合为一个元组 print('回调函数:',os.getpid())
print('ret-->',res) if __name__ == '__main__': p = Pool()
print('主进程:',os.getpid()) p.apply_async(func,args=(1,),callback=call_back) p.close() p.join()
#实例一: url_lst = ['http://www.baidu.com', 'http://www.souhu.com', 'http://www.sogou.com', 'http://www.4399.com', 'http://www.cnblogs.com'] import re from multiprocessing import Pool from urllib.request import urlopen def get_url(url): response = urlopen(url) ret = re.search('www.(.*?).com',url) print('%s finish'%ret.group(1)) return ret.group(1),response.read() def call(content): url,con = content with open(url+'.html','wb') as f: f.write(con) if __name__ == '__main__': p = Pool() for url in url_lst: p.apply_async(get_url,args=(url,),callback=call) p.close() p.join()
#实例二 import re from multiprocessing import Pool from urllib.request import urlopen def get_url(url): response = urlopen(url) ret = re.search('www.(.*?).com',url) print('%s finish'%ret.group(1)) return ret.group(1),response.read() def call(content): url,con = content with open(url+'.html','wb') as f: f.write(con) if __name__ == '__main__': p = Pool() p_lst = [] for url in url_lst: ret = p.apply_async(get_url,args=(url,)) #ret是一个内存地址 p_lst.append(ret) for ret in p_lst: call(ret.get())
三,线程
线程vs进程:
进程的概念:
1.计算机中最小的资源分配单位
2.进程对于操作系统来说还是有一定负担的
3.创建一个进程,操作系统要分配的资源大致有:
代码,数据,文件
线程的概念
1.轻量级
2.没有属于自己的进程资源
3.为什么要有线程
一条线程只负责执行代码,没有自己独立的代码,变量,文件资源
解决高并发的情况下,资源有限的问题
4.什么是线程:
线程是计算机中被cpu调度的最小单位
你的计算机当中的cpu都是执行的线程中的代码
5.线程和进程之间的关系
每一个进程中都有至少一条线程在工作
6.线程的特点
同一个进程中的所有线程的资源时共享的
轻量级:没有自己的资源
7.进程和线程之间的区别
占用的资源,线程之间共享进程内的资源,但是线程内的局部变量还是要占用资源
调度的效率:线程是计算机中被cpu调度的最小单位,调度速度快,开销小
资源是否共享:线程共享进程内的资源
可以并发执行
8.通用问题
java,c++,c#在一个进程中的多个线程能够并行
为什么python(c)中一个进程中的多个线程不能并行?
1.python是一个解释型的语言
2.cpython解释器,内部有一把全局解释器(GIL)
所以线程不能充分的利用多核
同一时刻同一个进程中的线程只有一个能被cpu执行
3.GIL锁:确实是限制了程序效率
4.GIL目前是能够帮助你在线程的切换中提高效率
5.asyncio模块
9.python的劣势,关于GIL的历史遗留问题
python不能实现,cpu利用率,极端的高计算型,需要多线程多核cpu高利用需求
解决办法:多进程,换一个解释器
#线程的代码实现:thread、threading #效率对比 import os import time from threading import Thread from multiprocessing import Process def func(i): # time.sleep(1) print('子线程%s'%i,os.getpid()) #多线程 # print('主进程',os.getpid()) if __name__ == '__main__': t_lst = [] start = time.time() for i in range(100): t = Thread(target=func,args=(i,)) t.start() t_lst.append(t) for t in t_lst: t.join() tt = time.time() - start # if __name__ == '__main__': start = 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() - start) print(tt)
# 多线程的数据共享 import os import time from threading import Thread from multiprocessing import Process num = 100 def func(): global num num -= 1 if __name__ == '__main__': t_lst = [] for i in range(100): t = Thread(target=func) t.start() t_lst.append(t) for t in t_lst: t.join() print(num)
#守护线程 #守护线程是在主线程结束之后,还等待了子线程执行结束才结束 #主线程结束,意味着主进程结束 #主线程等待所有的线程结束 #主线程结束之后,守护线程随着主进程的结束自然结束了 import time from threading import Thread def func1(): while True: time.sleep(0.5) print(123) def func2(): print('func2 start') time.sleep(3) print('func end') t1 = Thread(target=func1) t2 = Thread(target=func2) t1.setDaemon(True) t1.start() t2.start() print('主线程的代码结束')
#Thread实例对象的方法 # isAlive(): 返回线程是否活动的。 # getName(): 返回线程名。 # setName(): 设置线程名。 threading模块提供的一些方法: # threading.currentThread(): 返回当前的线程变量。 # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。 from threading import Thread import threading from multiprocessing import Process import os def work(): import time time.sleep(3) print(threading.current_thread().getName()) if __name__ == '__main__': #在主进程下开启线程 t=Thread(target=work) t.start() print(threading.current_thread().getName()) print(threading.current_thread()) #主线程 print(threading.enumerate()) #连同主线程在内有两个运行的线程 print(threading.active_count()) print('主线程/主进程') ''' 打印结果: MainThread <_MainThread(MainThread, started 140735268892672)> [<_MainThread(MainThread, started 140735268892672)>, <Thread(Thread-1, started 123145307557888)>] 主线程/主进程 Thread-1 '''
四,总结
#线程 #线程和进程之间的关系 #每个进程内都有一个线程 #线程是不能独立存在的 #线程和进程之间的区别 #同一个进程中线程之间的数据是共享的 #进程之间的数据是隔离的 #线程是由cpu执行的最小单位 #操作系统调度 #进程是计算机中最小的资源分配单位 #python #GIL锁 全局解释器锁 全局锁 #锁线程:同一时刻同一进程只会有一个线程访问cpu #锁的是线程而不是数据 #当程序是高IO形的 多线程 #当程序是高计算(cpu)型 多进程 #多进程数的范围:cpu*1 ~ cpu*2 #threading #Thread #守护线程:主线程结束之后才结束 #socketserver IO多路复用 + 多线程 #框架 并发的效果:多线程,协程的概念 flask #爬虫:线程池 协程