zoukankan      html  css  js  c++  java
  • python进程和线程(六)

    协程

            协程,又称微线程,纤程。英文名Coroutine。顾名思义,协程是协作式的,也就是非抢占式的程序(线程是抢占式的)。协程的关键字是yield,一看到这个就想到了生成器对不对?那就顺便回顾一下生成器的知识点,只要叫什么器的,那肯定就是函数形式,生成器也是函数,只不过多了一个yield在函数中:

    def foo():
        print('ok')
        yield
    
    foo()

            猜一下foo()执行我的结果是什么?结果是什么都不会打印,为什么呢?打印foo()是什么看一下:

    # -*- coding: utf-8 -*-
    
    def foo():
        print('ok')
        yield
    
    gen = foo()
    print(gen)
    
    >>><generator object foo at 0x000001AE31BCA318>

             原来foo()是一个生成器函数,只能通过next放方法执行了。

    # -*- coding: utf-8 -*-
    
    def foo():
        print('ok')
        yield
    
    gen = foo()
    # print(gen)
    gen.__next__()
    >>>ok

            这样才能打印出ok来。那除了__next__()方法,我们知道还有两种方法可以执行生成器函数:

    deffoo():print('ok')yieldprint('ok2')yield6#yield也可以返回内容,返回的内容给了调用他的那一步,相当于returngen=foo()next(gen)s=gen.__next__()print(s)#next()#只有两个yield,再执行这一步就会报错了>>>ok>>>ok2>>>6
    next()
    deffoo():print('ok')s=yield6print(s)print('ok2')yieldgen=foo()print(next(gen))gen.send('创给yield的值')#send也可以执行一次生成器,还可以传值给yield>>>ok>>>6>>>创给yield的值>>>ok2
    send()
    简单的生产者消费者模型

           为什么说yield是协程的关键字呢?因为协程是用户态的切换,就是说用户想什么时候切换就什么时候切换(这里的用户就是编程者),而yield刚好可以控制这一点。

    协程的两个优点:

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

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

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

            这里看不懂上面两个优点没关系,看下面的例子就能更好的理解了:

    1.yield方式简单实现协程

            这里将吃包子的人增加一个,看看yield怎么实现:

    import time
    
    def customer(name):
        print('等包子。。。')
        while True:
            baozi = yield
            print('%s拿到了包子%s'%(name,baozi))
    
    def producer(c1,c2):
        c1.__next__()
        c2.__next__()
        n = 0
        while True:
            time.sleep(1)   #做包子需要一点时间
            c1.send(n)
            c2.send(n+1)
            n += 2
    
    if __name__ == '__main__':
        c1 = customer('xiao')
        c2 = customer('bai')
        producer(c1,c2)
    View Code

    2.Greenlet方式

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

    from greenlet import greenlet
    
    def test1():
        print(1111)
        gr2.switch()
        print(2222)
        gr2.switch()
    
    
    def test2():
        print(3333)
        gr1.switch()
        print(4444)
    
    
    gr1 = greenlet(test1)
    gr2 = greenlet(test2)
    gr1.switch()
    View Code

            没有安装这个库的话,需要安装一下,然后看打印的结果,是不是可以控制随时切换啦,很神奇。

    1111
    3333
    2222
    4444

    3.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))
    
    # f('https://www.python.org/')
    # f('https://www.baidu.com/')
    # f('https://www.sina.com.cn/')
    # f("http://www.xiaohuar.com/hua/")
    
    gevent.joinall([
            gevent.spawn(f, 'https://www.python.org/'),
            gevent.spawn(f, 'https://www.baidu.com/'),
            gevent.spawn(f, 'https://www.sina.com.cn/'),
            gevent.spawn(f, 'http://www.xiaohuar.com/hua/'),
    ])
    
    
    print("cost time:",time.time()-start)
    View Code

          大家可以比较一下使用协程和直接按顺序执行有没有提升效率。

          到了这里,协程的基础知识也说完了,还有很多专门开发关于协程的库,因为协程很有作用,这里的例子很简单,但是这里面门道很深的。进程和线程的内容,都是死的,但是协程可以灵活运用,需要不断的去研究创新的。

  • 相关阅读:
    vim复制
    嵌入式Linux学习(二)
    (Java实现) 洛谷 P1042 乒乓球
    (Java实现) 洛谷 P1042 乒乓球
    (Java实现) 洛谷 P1071 潜伏者
    (Java实现) 洛谷 P1071 潜伏者
    (Java实现) 洛谷 P1025 数的划分
    (Java实现)洛谷 P1093 奖学金
    (Java实现)洛谷 P1093 奖学金
    Java实现 洛谷 P1064 金明的预算方案
  • 原文地址:https://www.cnblogs.com/pengfy/p/10742248.html
Copyright © 2011-2022 走看看