zoukankan      html  css  js  c++  java
  • Python学习第52天(协程)

      关于协程,最早出现在处理io密集型和计算密集型问题中,出现过多进程加协程的处理模式,首先关于协程,其本质就是一个微线程。

      先说一下之前我们学到的线程和进程,这两者有一个非常明显和共同的特点,都属于抢占式程序,之间的切换是完全不受我们控制的,执行切换过程是属于操作系统级别的操作,所以与之对应,协程就出现了,其实协程的本质就是在你控制下进行线程之间的切换,其实我们在之前的生成器部分有讲到一个yield,就有类似的效果。

      先来回顾一下yield的使用方法吧

    def f():
        print('hello world 1')
        yield
        print('hello world 2')
        yield
        print('hello world 3')
    
    gen = f()
    print(type(gen)) gen.
    __next__() gen.__next__() gen.__next__()

      当含有yield的函数在最初运行的时候,预运行的时候会被系统识别,正式开始运行的时候,不是直接开始输出其中的语句,而是生成一个生成器,随后我们可以通过内置方法next()或者__next__()方法调用,每调用一次就会输出到yield处,随后保存运行的状态,待下一次运行next()的时候继续从上一次的next处向下运行,遇到下一个yield时保存状态,停止运行,同时yield也有return的功能,同样可以返回参数。

      其实yield的功能是类似于我们的协程的,所以我们先试用yield实现以下协程的功能。

    import time
    import queue
    
    def consumer(name):
        print("--->ready to eat baozi...")
        while True:
            new_baozi = yield
            print("[%s] is eating baozi %s" % (name,new_baozi))
            #time.sleep(1)
    
    def producer():
    
        r = con.__next__()
        r = con2.__next__()
        n = 0
        while 1:
            time.sleep(1)
            print("33[32;1m[producer]33[0m is making baozi %s and %s" %(n,n+1) )
            con.send(n)
            con2.send(n+1)
    
            n +=2
    
    
    if __name__ == '__main__':
        con = consumer("c1")
        con2 = consumer("c2")
        p = producer()

      虽然能够基本实现,但是我们在实际操作的过程总不能每切换一次就是用一次next吧,太麻烦了,所以我们引入了greenlet模块

      greenlet是一个用C实现的协程模块,相比与python自带的yield,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator

      看一下实例吧。

    from greenlet import greenlet
     
     
    def test1():
        print(12)
        gr2.switch()
        print(34)
        gr2.switch()
     
     
    def test2():
        print(56)
        gr1.switch()
        print(78)
     
     
    gr1 = greenlet(test1)
    gr2 = greenlet(test2)
    gr1.switch()

      虽然比yield好了很多,可以实现任意函数之间的切换,但是每次切换都需要人为的操作,不好不好,所以我们也要引入不用自己切换的这种模式。

      gevent模块

    import gevent
    
    import requests,time
    
    
    start=time.time()
    
    def f(url):
        print('GET: %s' % url)
        resp =requests.get(url)
        data = resp.text
        print('%d bytes received from %s.' % (len(data), url))
    
    gevent.joinall([
    
            gevent.spawn(f, 'https://www.python.org/'),
            gevent.spawn(f, 'https://www.yahoo.com/'),
            gevent.spawn(f, 'https://www.baidu.com/'),
            gevent.spawn(f, 'https://www.sina.com.cn/'),
    
    ])
    
    # f('https://www.python.org/')
    #
    # f('https://www.yahoo.com/')
    #
    # f('https://baidu.com/')
    #
    # f('https://www.sina.com.cn/')
    
    print("cost time:",time.time()-start)

      大致就是这个样子的,这就有点类似多线程的时候了,所以我们可以综合使用嘛。

      现在可以总结一下协程的优势了:

        优点1: 协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。

        优点2: 不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。

        因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。

    以上就是主要内容部分,其实前面的知识现在开始忘的连点印象都没有了,所以,复习复习,很重要。

    最近工作也需要自己投入更多,加油加油。

  • 相关阅读:
    Flink--Table和DataStream和DataSet的集成
    flink-SQL
    Flink的容错
    Flink--基于mysql的sink和source
    Flink--sink到kafka
    Flink在流处理上常见的Source和sink操作
    【计算机网络】-传输层-传输服务的要素
    【计算机网络】-传输层-传输服务
    文件系统-文件的逻辑结构与存取方法
    文件系统-概念
  • 原文地址:https://www.cnblogs.com/xiaoyaotx/p/12702498.html
Copyright © 2011-2022 走看看