队列
队列:先进先出,数据进程安全
管道 + 锁
生产者消费者模型:解决数据供需不平衡
多个进程取值,不会发生数据不一致。数据进程安全
队列实现方式是 管道 + 锁
管道
双向通信 数据进程不安全
EOFError:
管道是由操作系统进行引用计数的。
必须在所有进程中关闭管道后才能生成EOFError 异常
管道和数据共享,数据是不安全的。
数据共享(不常用)
Manager
list dict 数据进程不安全的
队列是内置锁,所以别的应用调用它,是安全的。
凡是涉及到手动加锁的,都是不安全的。常用的一般都是消息中间件
进程池:
存放进程的容器
在进程创建之初,创建固定个数的进程在执行
会被多个任务循环利用
节省了进程创建和销毁的时间开销
降低了操作系统调度进程的压力
信号量和进程池的区别
信号量n个任务开启n个进程。
但同一时间只能有固定个数的进程在执行
进程池 n 个任务开启固定个数的进程
因此同一时间只能有固定个数的进程在执行
例如:200个任务--200个进程 4个进程执行 信号量
200个任务--4个进程 4个进程执行 进程池
一个进程是等待,一个是任务等待。
信号量 n 个任务开启n个进程。
但同一时间只能有固定个数的进程在执行
进程在等待被执行
进程池 n 任务开启固定个数的进程
因此同一时间只能有固定个数的进程在执行
任务在等待被执行
import time from multiprocessing import Pool def wahaha(i): time.sleep(1) print('*'*i) if __name__ == "__main__": p = Pool(5) p.map(func=wahaha,iterable=range(10))
说明:
自带close和join
但是参数必须是一个iterable
不能获取返回值
import time
from multiprocessing import Pool
def wahaha(i):
time.sleep(1)
print('*'*i)
if __name__ == "__main__":
p = Pool(5)
p.map(func=wahaha,iterable=range(10))
res_l = []
for i in range(10):
res = p.apply_async(func=wahaha,args=[i,])
res_l.append(res)
for i in res_l:print(i.get())
p.close() # 不能再提交新的任务
p.join() #等待池中的任务都执行完
*
**
***
****
******
*****
*******
********
*********
*
***
**
None
None
None
None
****
None
******
*****
None
None
********
*********
*******
None
None
None
主进程默认等待子进程结束----守护进程
普通的进程:根据你调用的函数执行结束它就结束了
进程池里的进程
没有返回值
在提交任务之后:
p.close() #不能在提交新的任务
p.join() #等待池中的任务都执行完
有返回值的时候:
在提交任务之后:
for i in res_l:print(i.get())
回调函数
回调函数在什么时候执行
子进程的任务执行完毕以后立即触发
回调函数的参数
子进程的返回值
回调函数是由谁执行的
主进程执行的
在哪儿用?
爬虫:
如果要爬取多个格式相同的网页
真正影响程序效率的是网络的延迟
计算 分析 处理网页的时间是很快的
#500
有回调函数
没有回调函数快
进程:操作系统管理进程,进程是执行任务,资源的隔离。在一个操作系统中,同一时间,有多个任务。多个任务之间的内存必须隔离开。使用QQ的时候,还能使用微信
并发
并发的要求日益增加,聊天----开子进程。你可以看电影-----开子进程,缓存其他电影。开启一个子进程的开销,是很大的。操作系统在进程之间的切换 ,时间开销也很大。
进程之间的通信
数据共享:时间开销
如果多个子进程之间的数据共享量过多的时候,就不应该将这些数据隔离开。一个进程-----实现不了并发。
你既不希望数据隔离,还要实现并发的效果。
线程
线程是轻量级的进程,线程的创建和销毁所需要的时间开销都非常小,线程直接使用进程的内存,线程不能独立存在,要依赖于进程。
进程---资源分配的最小单位
线程---CPU调度的最小单位
轻型进程:创建、销毁、切换、开销比进程小
数据不隔离
可以并发
依赖进程
每一个进程里至少有一个线程,进程负责资源、线程负责执行代码。
python程序运行起来------进程
进程-----管理整个程序的内存。
存储全局的变量:内置的函数 全局的名字
线程-----执行代码
利用python开启多线程---------并发
import os
import time
from threading import Thread
from multiprocessing import Process
def func(i):
print('---->',os.getppid())
time.sleep(1)
print('*',i)
if __name__=='__main__':
print(os.getppid())
start = time.time()
thread_lst = []
for i in range(20):
t = Thread(target=func,args=(i,))
t.start()
print('---->',t.name,t.ident)
thread_lst.append(t)
for t in thread_lst:t.join()
print(time.time()-start)
start = time.time()
process_lst = []
for i in range(20):
p = Process(target=func,args=(i,))
p.start()
process_lst.append(p)
for p in process_lst:p.join()
print(time.time()-start)
执行输出:
3624
----> 3624
----> Thread-1 2168
----> Thread-2 1600
----> 3624
----> 3624
----> Thread-3 13856
----> 3624
----> Thread-4 6100
----> 3624
----> Thread-5 3712
----> 3624
----> Thread-6 14408
----> 3624
----> Thread-7 4480
----> 3624
----> Thread-8 3760
----> 3624
----> Thread-9 16268
----> Thread-10 11848
----> 3624
----> Thread-11 4244
----> 3624
----> Thread-12 13804
----> 3624
----> Thread-13 5788
----> 3624
----> Thread-14 1028
----> 3624
----> Thread-15 6608
----> 3624
----> Thread-16 15312
----> 3624
----> Thread-17 14580
----> 3624
----> Thread-18 6564
----> 3624
----> Thread-19 11672
----> 3624
----> Thread-20 11396
----> 3624
* 0
* 1
* 2
* 3
* 4
* 5
* 6
* 7
* 8
* 9
* 10
* 11
* 12
* 13
* 14
* 15
* 16
* 17
* 18
* 19
1.107273817062378
----> 4468
----> 4468
----> 4468
----> 4468
----> 4468
----> 4468
----> 4468
----> 4468
----> 4468
----> 4468
----> 4468
----> 4468
----> 4468
----> 4468
----> 4468
----> 4468
----> 4468
----> 4468
----> 4468
----> 4468
* 0
* 1
* 3
* 2
* 4
* 5
* 6
* 7
* 9
* 8
* 10
* 11
* 15
* 12
* 13
* 16
* 14
* 18
* 17
* 19
1.9786567687988281
效率问题:线程快 进程慢
同一个进程下的多个线程进程号相同:线程号不同
if __name__ == '__main__' : 开启进程 必须有这句话 但是开启线程不需要
这种现象只在windows操作系统上才出现
数据的共享问题:在进程之间数据隔离,在线程之间数据共享
def manager():
global n
from threading import Thread
n = 100
t = Thread(target=manager)
t.start()
t.join()
print('-------->',n)
GIL —— 全局解释器锁
锁线程 :在计算的时候 同一时刻只能有一个线程访问CPU
线程锁限制了你对CPU的使用,但是不影响web类或者爬虫类代码的效率
我们可以通过启动多进程的形式来弥补这个问题
import time from threading import Thread def func1(): while True: time.sleep(1) print('子线程') def func2(): time.sleep(5) print('子线程2') t = Thread(target=func1) t2 = Thread(target=func2) t.setDaemon(True) t.start() t2.start() print('主线程')
# 主进程的守护进程是在主进程的代码结束,守护进程就结束了
# 主线程的守护线程会在非守护线程的所有线程执行完毕之后才结束