初识进程 线程 协程:协程

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

 

以上是关于初识进程 线程 协程:协程的主要内容,如果未能解决你的问题,请参考以下文章

进程和线程和协程之间的关系

多线程 多进程 协程 Queue(爬虫代码)

协程

python 多进程,多线程,协程

进程_线程 之 --- 协程

进程线程和协程的区别