线程队列,协程基础
线程队列
Queue
- 普通的容器,不具备IPC的能力
- 用法和进程类似,有join方法,原理等同于joinableQueue
LifoQueue
- 后进先出,用来模拟栈。
- 与Queue的区别仅是顺序不同
PriorityQueue
- 具备优先级的队列,取出数据时,越小的优先级越高
运算符重载
- 使得对象可以被比较
__lt__
:小于,__gt__
:大于
class Person:
def __init__(self, age, name):
self.age = age
self.name = name
# 覆盖比较运算符,当在两个对象之间使用比较运算符时,会自动触发
def __lt__(self, other):
if self.age == other.age:
return self.name < other.name
return self.age < other.age
p1 = Person(17, 'haha')
p2 = Person(19, 'lala')
print(p1 < p2)
协程
GIL 导致Cpython中多线程无法并发执行,只能并发执行,效率低
由于GIL锁在多个线程间只能切换执行,创建以及销毁线程以及线程切换需要消耗资源。
并且最主要的问题,多线程容易出现假死问题。
例如:TCP服务器限制最大线程数量为1000,如果这1000个客户有一部分没有进行任务的操作,而新任务就无法被处理,即使CPU是空闲的。
办法:让单个线程负责处理所有任务,从而避免重复创建销毁,以及最大数量限制的问题
单线程实现并发处理
首先并发的原理是多道技术,原理也就是切换+保存状态
任务也就是一个函数,那能否在多个函数之间切换?
使用yield就可以实现,在多个函数之间切换执行
def func1():
for i in range(20):
print(1)
yield
def func2():
for i in range(20):
f1.__next__()
print(2)
f1 = func1()
f2 = func2()
这样做虽然实现了并发,但是效率反而大大降低了,并且遇到IO操作时,会停下而不会并发。
并且使用生成器来处理多任务的话,代码结构会很混乱
Greenlet模块
第三方的模块,主要就封装了生成器,可以使得在多个函数之间切换更为方便
这个模块也仅仅只是封装了生成器让语法更简单,并没有实现自动检测IO操作自动切换
import greenlet
def func1():
for i in range(3):
print(1)
g2.switch()
def func2():
for i in range(3):
print(2)
g1.switch()
g1 = greenlet.greenlet(func1)
g2 = greenlet.greenlet(func2)
g2.switch()
Gevent
在Greenlet的基础上又进行了一次封装
- 仔细看代码,里面有详细的注释
from gevent import monkey
monkey.patch_all(ssl=False) # 这里就相当于集成了各种阻塞的模块,然后进行一些修改,True就是修改,我的ssl报错,所以我关了,记得在使用模块前打补丁(不是调用模块前)
import time,gevent
def func1():
print('func1 start')
time.sleep(3)
print('func1 over')
def func2():
print('func2 start')
print('func2 over')
# spawn用来提交任务
# 写成任务都是以异步方式提交
g1 = gevent.spawn(func1) # 有参数直接往func1后面填写就可以
g2 = gevent.spawn(func2)
# 因为主程序结束,这两个函数也会结束,所以要保证主程序存活
g1.join() # 主程序会阻塞在这里,等待g1结束。
gevent.joinall((g1, g2)) # 主程序等待g1,g2结束
# 想要让那两个任务执行,不光主程序不能挂,而且还要出现阻塞的状态,不然也没用
# while True: # 这样做主程序也不会挂,但是那两个任务开不起来
# pass
# whilt True: # 这样做可以
# time.sleep(0.2)
def func3():
while True:
time.sleep(0.2)
g3 = gevent.spawn(func3)
g3.join() # 这样封装起来也可以
g1.value # 这样可以拿到g1的返回值
协程的定义
协程是什么,本质就是在单个线程下实现并发效果,即在多个任务之间做切换
协程又称之为,微线程,是由应用程序自己来控制切换的任务处理方式。
Python中线程是本地线程,就是操作系统级别的线程,是由操作系统来控制调度的。
协程的优势:协程对比线程更加轻量化
1. 开销更小,如果我们能在当前任务遇到IO时切换其他任务,就可以尽可能多利用CPU如果你的任务足够多,就可以将CPU利用直到超时为止。
2. 避免了线程数量上限的问题
协程的缺点:
1. 无法利用多核CPU的优势。
什么时候用协程
IO密集型任务,且任务数量非常大
注意事项:
- 协程任务都是以异步方式提交,主线程依旧会继续往下执行,而一旦执行完最后一行,主线程也就结束了。所以还有任务要执行,需要保持主线程存活,并且要有阻塞的状态。
- monkey补丁的原理是把原始的阻塞方法替换为修改后的非阻塞方法,来实现IO自动切换。
我们可以用threading.current_thread().getName()来查看每个g1和g2,查看的结果为DummyThread-n,即假线程