协程

作者: 沉吟不语 | 来源:发表于2018-11-27 19:29 被阅读11次

    协程是什么?

    协程是python个中另外一种实现多任务的方式,只不过比线程更小占用更小执行单元(理解为需要的资源)。 为啥说它是一个执行单元,因为它自带CPU寄存器上下文。这样只要在合适的时机, 我们可以把一个协程 切换到另一个协程。 只要这个过程中保存或恢复 CPU上下文那么程序还是可以运行的。

    协程和线程差异

    在实现多任务时, 线程切换从系统层面远不止保存和恢复 CPU上下文这么简单。 操作系统为了程序运行的高效性每个线程都有自己缓存Cache等等数据,操作系统还会帮你做这些数据的恢复操作。 所以线程的切换非常耗性能。但是协程的切换只是单纯的操作CPU的上下文,所以一秒钟切换个上百万次系统都抗的住。
    通俗的理解:在一个线程中的某个函数,可以在任何地方保存当前函数的一些临时变量等信息,然后切换到另外一个函数中执行,注意不是通过调用函数的方式做到的,并且切换的次数以及什么时候再切换到原来的函数都由开发者自己确定。

    迭代器

    迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
    可迭代对象: 我们已经知道可以对list、tuple、str等类型的数据使用for...in...的循环语法从其中依次拿到数据进行使用,我们把这样的过程称为遍历,也叫迭代。

    # 列表,元组,集合,字典,字符串都是可迭代对象
    
    data_list = [1,2,3,4,5,6,7]
    
    # for i in data_list:
    #     print(i)
    
    data_obj = iter(data_list)
    print(next(data_obj))
    
    data_int = 10
    
    # for i in data_int:
    #     # 'int' object is not iterable
    #     print(i)
    
    #判断某个对象是否是可迭代对象
    from collections import Iterable
    
    print(isinstance(data_list,Iterable))
    print(isinstance(data_int,Iterable))
    
    

    生成器

    利用迭代器,我们可以在每次迭代获取数据(通过next()方法)时按照特定的规律进行生成。但是我们在实现一个迭代器时,关于当前迭代到的状态需要我们自己记录,进而才能根据当前状态生成下一个数据。为了达到记录当前状态,并配合next()函数进行迭代使用,我们可以采用更简便的语法,即生成器(generator)。生成器是一类特殊的迭代器。

    # #生成器是一种特殊的迭代器
    
    # #如何构建一个生成器
    # data_tupe = (1,2,3,4,5,6,7)
    
    # #通过元组,我们可以得到一个生成器对象
    # data_obj = (i for i in data_tupe if i > 3)
    # print(data_obj)
    
    
    # print(next(data_obj))
    
    # 方式二
    # 采用yeild
    
    boos_data = []
    
    def set_books(num):
        current_num = 0
        while num > current_num:
            # print('书籍',current_num)
            current_num+=1
            # boos_data.append('书籍'+str(current_num))
            yield '书籍'+str(current_num)
    
    obj = set_books(100)
    
    for book in obj:
    
        print(book)
    
    
    

    yeild实现简单实现协程

    import time
    
    def work1():
        while True:
            print("----work1---")
            yield
            time.sleep(0.5)
    
    def work2():
        while True:
            print("----work2---")
            yield
            time.sleep(0.5)
    
    def main():
        w1 = work1()
        w2 = work2()
        while True:
            next(w1)
            next(w2)
    
    if __name__ == "__main__":
        main()
    

    greenlet

    为了更好使用协程来完成多任务,python中的greenlet模块对其封装,从而使得切换任务变的更加简单
    1.安装方式 使用如下命令安装greenlet模块:

    sudo pip3 install greenlet

    2.greenlet的使用

    from greenlet import greenlet
    import requests
    
    def download1():
        print('正在下载1')
        #耗时的操作
        response = requests.get(url='https://github.com/')
        gre2.switch()
        print('download1下载完了')
        gre2.switch()
    
    
    def download2():
        print('正在下载2')
        response = requests.get(url='https://github.com/')
        gre1.switch()
        print('download2下载完了')
    
    
    gre1 = greenlet(download1)
    gre2 = greenlet(download2)
    gre1.switch()
    

    gevent

    • greenlet已经实现了协程,但是这个还的人工切换,是不是觉得太麻烦了,不要捉急,python还有一个比greenlet更强大的并且能够自动切换任务的模块gevent
    • 其原理是当一个greenlet遇到IO(指的是input output 输入输出,比如网络、文件操作等)操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。
    • 由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO

    1.安装 pip3 install gevent

    2.gevent的使用

    #gevent能够在内部自己实现携程之间的切换
    import gevent
    
    def f(n):
        for i in range(n):
            print(gevent.getcurrent(), i)
            #用来模拟一个耗时操作,注意不是time模块中的sleep
            gevent.sleep(1)
    
    g1 = gevent.spawn(f, 5)
    g2 = gevent.spawn(f, 5)
    g3 = gevent.spawn(f, 5)
    g1.join()
    g2.join()
    g3.join()
    

    总结

    1.进程是资源分配的单位
    2.线程是操作系统调度的单位
    3.进程切换需要的资源很最大,效率很低
    4.线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)
    5.协程切换任务资源很小,效率高
    6.多进程、多线程根据cpu核数不一样可能是并行的,但是协程是在一个线程中所以是并发的.

    相关文章

      网友评论

          本文标题:协程

          本文链接:https://www.haomeiwen.com/subject/wpenqqtx.html