首先要了解两个概念
上下文
在每个任务运行前,CPU都需要知道任务从哪里加载,又是从哪里开始运 行,也就是说,需要系统事先帮他设置好CPU寄存器和程序计数器(Program Counter,PC)
CPU寄存器,是CPU内置的容量小、但速度极快的内存。程序计数器,则是用来存储CPU正在执行的指令的位置,或者即将执行的下一条指令的位置。他们都是CPU在运行任何任务前,必须依赖的环境,因此也被叫做CPU上下文。
知道了什么是CPU上下文,也就很容易理解CPU上下文切换。CPU上下文切换,就是先把前一个任务的CPU上下文(也就是CPU寄存器和程序计数器)保存起来,然后加载新任务的上下文,到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务
IO操作
网络io: 获取或者上传网络数据(下载或者上传)
磁盘io : 读取写入文件
io操作比较耗时,如果程序io设计不好,非常影响性能
协程 又称 微线程,纤程
协程的实际概念
协程是python中实现多任务的一种方式,是比线程跟小的执行单元,
因为它自带CPU上下文,会在合适的时机,把一个协程切换到另一个协程,所以它是有一个执行单元,并且这个过程中保存或恢复CPU上下文的程序还是可以运行。
这个合适的时机很重要由开发者自己确定
下面我们使用简单的方式实现协程
import time
def test1():
while True:
print("text1")
yield
# 一个耗时操作 合适的时机
time.sleep(0.5)
def test2():
while True:
print("text2")
yield
# 一个耗时操作 合适的时机
time.sleep(0.5)
t1 = test1()
t2 = test2()
while True:
next(t1)
next(t2)
应该很容易看出这个运行结果, 如果不知道yield,可以先去去了解一下生成器这个概念。
这个就是协程的简单实现,然而我们日常用的一般是已经封装好的
gevent
除了gevent还有一个greenlet也实现了协程,但是他还是需要人工切换。
gevent是一个可以自行切换任务的模块,它的原理是当一个程序遇到IO操作时,会切换协程,保证别的程序还能运行,而不是等待IO,从而浪费了等待的时间。
简单实现
import gevent
def test1(n):
for i in range(n):
print('test1----', i)
# 模拟一个耗时操作 必须用gevent的sleep(1)
gevent.sleep(1)
t1 = gevent.spawn(test1, 5)
t2 = gevent.spawn(test1, 5)
t3 = gevent.spawn(test1, 5)
t1.join()
t2.join()
t3.join()
# 下面方式与上面相同
#gevent.joinall([
# gevent.spawn(test1, 5),
# gevent.spawn(test1, 5),
# gevent.spawn(test1, 5)
#])
运行结果
具体运行效果建应自行尝试
一般情况下,为了避免阻塞自动切换协程,程序启动时要执行 monkey.patch_all()解决
import time
import gevent
from gevent import monkey
monkey.patch_all()
def test1(n):
for i in range(n):
print('test1----', i)
# 因为 执行了 monkey.path_all() 所以当使用time来模拟耗时时也开始实现
time.sleep(1)
gevent.joinall([
gevent.spawn(test1, 5),
gevent.spawn(test1, 5),
gevent.spawn(test1, 5)
]
)
协程的大致情况就是如此,接下来我会在写一个用协程实现的并发下载器
如对本文有疑问,
点击进行留言回复!!
您可能感兴趣的文章:
第二章 如何利用Python读取Oracle表数据和表头转化为字典类型
荐 Python基础知识(一):变量与赋值、运算符、数据类型及位运算
python漫画爬虫:我不做人了,b站!爬取辉夜大小姐等漫画
【LeeCode 中等 数学 python3】剑指 Offer 43. 1~n整数中1出现的次数
网友评论