初识进程 线程 协程:协程
Posted xiaoqichaoren
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了初识进程 线程 协程:协程相关的知识,希望对你有一定的参考价值。
协程:(又称微线程,也是交替运行)
进程-->线程-->协程
协程就是充分利用cpu给该线程的时间,多个协程只使用一个线程,某个任务遇到阻塞,执行下一个任务。
如果一个线程只执行一个任务,比较容易进入阻塞队列,如果这条线程永远在工作(协程:一个线程执行多个任务),永远不会进入阻塞队列。
适用场景:
当程序中存在大量不需要CPU的操作时(IO)
特点:
每次都能从上次暂停的位置继续执行
三种实现方式:
1.yield(生成器)
生成器:一边计算一边循环的机制
def a(): ...... # 只会执行一次 while True: ...... yield ...... next(a()) # 只执行一次循环,到yield返回 next(a()) # 从yield之后执行 ......
2.greenlet
可以按照一定的顺序执行多个任务,但是顺序是人为规定的
def a(): while True: ...... B.switch() ...... def b(): while True: ...... C.switch() ...... def c(): while True: ...... A.switch() ...... if __name__ == ‘__main__‘: A = greenlet(a) B = greenlet(b) C = greenlet(c) A.switch() # 可以自动按一定顺序执行(无间断执行),但是需要手动将代码编写好
在greenlet中关于switch的官方解释是 Switch execution to this greenlet.
也就是可以视为开始执行(start)
3.gevent
任务是同时开始的,执行顺序取决于任务的阻塞程度(非阻塞任务优于阻塞任务)
monkey.patch_all() # 猴子补丁,与gevent搭配使用,当一个任务发生阻塞时,立即执行别的任务,若不加猴子补丁会一直等待任务完成,失去了协程的意义 def a(): while True: ...... sleep() # 代表的是任务阻塞,比如:网络延迟等 def b(): while True: ...... sleep() def c(): while True: ...... sleep() if __name__ == ‘__main__‘: A = gevent.spawn(a) B = gevent.spawn(b) C = gevent.spawn(c) gevent.joinall([A, B, C]) # 当某一任务发生阻塞,立即切换别的任务
gevent与猴子补丁(monkey.patch_all())要一起使用:
当一个任务发生阻塞时,立即执行别的任务,若不加猴子补丁会一直等待某一任务完成,失去了协程的意义
ps:greenlet和gevent中任务的动作可以一样也可以不一样
关于yield
可以暂停循环,下一次进入循环时,可以从暂停的位置继续执行
def foo(): print(‘starting...‘) while True: res = yield 4 # yield两个功能:1.返回某值,与return类似 2.执行next时从yield之后开始而不是从函数头开始,直到再次循环到yield print(‘res:‘, res) g = foo() print(next(g)) # 一次返回一个结果而不是一次性返回所有结果 print(‘*‘*20) print(next(g)) # 可以按需求返回某些结果,节省了内存
‘‘‘
运行结果:
starting...
4
********************
res: None
4
‘‘‘
1.程序开始执行以后,因为foo函数中有yield关键字,所以foo函数并不会真的执行,而是先得到一个生成器g(相当于一个对象)
2.直到调用next方法,foo函数正式开始执行,先执行foo函数中的print方法,然后进入while循环
3.程序遇到yield关键字,然后把yield想想成return,return了一个4之后,本次循环停止,并没有执行赋值给res操作,
此时next(g)语句执行完成,所以输出的前两行:第一个是while上面的print的结果,第二个是return出的结果(是执行print(next(g))的结果,)
4.程序执行print("*"*20),输出20个*
5.又开始执行下面的print(next(g)),这个时候和上面那个差不多,不过不同的是,这个时候是从刚才那个next程序停止的地方开始执行的,
也就是要执行res的赋值操作,这时候要注意,这个时候赋值操作的右边是没有值的(因为刚才那个是return出去了,并没有给赋值操作的左边传参数),
所以这个时候res赋值是None,所以接着下面的输出就是res:None,
6.程序会继续在while里执行,又一次碰到yield,这个时候同样return 出4,然后程序停止,print函数输出的4就是这次return出的4.
print(g.send(‘哈哈‘)) # send函数:将值赋给需要赋值的变量,(如:res) print(‘over‘)
‘‘‘
运行结果:
res: 哈哈
4
over
‘‘‘
7.程序执行g.send(‘哈哈‘),程序会从yield关键字那一行继续向下运行,send会把 哈哈 这个值赋值给res变量
8.由于send方法中包含next()方法,所以程序会继续向下运行执行print方法,然后再次进入while循环
9.程序执行再次遇到yield关键字,yield会返回后面的值后,程序再次暂停,直到再次调用next方法或send方法。
示例:
# 生成1-10的数字
def foo(num): print("numbers:1-9") while num < 10: num += 1 yield num for n in foo(0): print(n)
for n in range(1, 11): 这种方法在range(100)时,默认生成一个长度为10的list,太占用内存
for n in foo(0): 而用生成器生成10个数时,只是一个生成器对象foo(0),内存占用不大
foo(0):0是一个初始值,经过生成器可以返回1-10的数字。初始值是任意的,比如:6,则生成器返回的值为7-17
以上是关于初识进程 线程 协程:协程的主要内容,如果未能解决你的问题,请参考以下文章