当前位置: 移动技术网 > IT编程>脚本编程>Python > 协程--Python

协程--Python

2020年07月20日  | 移动技术网IT编程  | 我要评论

协程

首先要了解两个概念

  1. 上下文

    在每个任务运行前,CPU都需要知道任务从哪里加载,又是从哪里开始运 行,也就是说,需要系统事先帮他设置好CPU寄存器和程序计数器(Program Counter,PC)

    CPU寄存器,是CPU内置的容量小、但速度极快的内存。程序计数器,则是用来存储CPU正在执行的指令的位置,或者即将执行的下一条指令的位置。他们都是CPU在运行任何任务前,必须依赖的环境,因此也被叫做CPU上下文。

    知道了什么是CPU上下文,也就很容易理解CPU上下文切换。CPU上下文切换,就是先把前一个任务的CPU上下文(也就是CPU寄存器和程序计数器)保存起来,然后加载新任务的上下文,到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务

  2. 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)
]
)

协程的大致情况就是如此,接下来我会在写一个用协程实现的并发下载器

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网