zoukankan      html  css  js  c++  java
  • Python学习---协程 1226

    协程【是一个单线程】,又称微线程,纤程。英文名Coroutine。

    一句话说明什么是协程:协程是一种用户态的轻量级线程【程序员自己去切换线程】

    协程条件:

    必须在只有一个单线程里实现并发

    修改共享数据不需加锁

    用户程序里自己保存多个控制流的上下文栈

    一个协程遇到IO操作自动切换到其它协程

    协程原理:

    协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:

    协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。

    协程的好处Nginx就是协程】:

    无需线程上下文切换的开销【单线程

    无需原子操作锁定及同步的开销

    "原子操作(atomic operation)是不需要synchronized",所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序是不可以被打乱,或者切割掉只执行部分。视作整体是原子性的核心。

    方便切换控制流,简化编程模型

    高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理

    协程缺点:

    无法利用多核资源【可以通过多进程实现多核利用】:协程的本质是个单线程,它不能同时将单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。

    进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序

    最底层实际上使用yield实现协程操作

    import time
    import queue
    def consumer(name):
        print("--->starting eating 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 n < 5:
            n += 1
            con.send(n)
            con2.send(n)
            print("33[32;1m[producer]33[0m is making baozi %s" % n)
    if __name__ == '__main__':
        con = consumer("c1")
        con2 = consumer("c2")
        p = producer()

    gevent

    安装gevent

    image

    Gevent 介绍

         Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。

    import gevent
    import time
    def func1():
        print(time.ctime(), '33[31;1mA-->B...33[0m')
        gevent.sleep(2)   # 模拟IO阻塞
        print(time.ctime(), '33[31;1mA-->B...33[0m')
    def func2():
        print(time.ctime(), '33[32;1mB-->A...33[0m')
        gevent.sleep(1)      # 模拟IO阻塞
        print(time.ctime(), '33[32;1mB-->A...33[0m')
    # 一个线程自己切换,共耗时2秒,以最大的为准
    gevent.joinall([
        gevent.spawn(func1),
        gevent.spawn(func2),
    ])

    image

    Greenlet

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

    # -*- coding:utf-8 -*-
    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()

    image

    协程爬虫

    遇到IO阻塞时会自动切换任务[协程爬虫]

    from gevent import monkey;
    monkey.patch_all()   # 监听IO阻塞
    import time
    import gevent
    from  urllib.request import urlopen
    start = time.time()
    def f(url, name):
        print('GET: %s' % url)
        resp = urlopen(url)
        data = resp.read()
        with open(name+'.thml', 'wb') as f:
            f.write(data)
        print('%d bytes received from %s.' % (len(data), url))
    gevent.joinall([
        gevent.spawn(f, 'https://www.python.org/', 'python'),  # 就是这种格式传递参数
        gevent.spawn(f, 'https://www.yahoo.com/', 'yahoo'),
        gevent.spawn(f, 'https://github.com/', 'github'),
    ])
    end = time.time()
    print('gevent 耗时', str(end-start))

    image

    【更多参考】

    http://www.cnblogs.com/alex3714/articles/5248247.html

  • 相关阅读:
    UVA 254 Towers of Hanoi
    UVA 701 The Archeologists' Dilemma
    UVA 185 Roman Numerals
    UVA 10994 Simple Addition
    UVA 10570 Meeting with Aliens
    UVA 306 Cipher
    UVA 10160 Servicing Stations
    UVA 317 Hexagon
    UVA 10123 No Tipping
    UVA 696 How Many Knights
  • 原文地址:https://www.cnblogs.com/ftl1012/p/9384297.html
Copyright © 2011-2022 走看看