zoukankan      html  css  js  c++  java
  • python_day10 协程

    协程:是单线程下的并发,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。

    需要强调的是:

      1. python的线程属于内核级别的,即由操作系统控制调度(如单线程一旦遇到io就被迫交出cpu执行权限,切换其他线程运行)

      2. 单线程内开启协程,一旦遇到io,从应用程序级别(而非操作系统)控制切换

    对比操作系统控制线程的切换,用户在单线程内控制协程的切换,优点如下:

      1.  协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级

      2. 单线程内就可以实现并发的效果,最大限度地利用cpu

    要实现协程,关键在于用户程序自己控制程序切换,切换之前必须由用户程序自己保存协程上一次调用时的状态,如此,每次重新调用时,能够从上次的位置继续执行

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

    为此,我们之前已经学习过一种在单线程下可以保存程序运行状态的方法,即yield,我们来简单复习一下:

    1. yiled可以保存状态,yield的状态保存与操作系统的保存线程状态很像,但是yield是代码级别控制的,更轻量级
    2. send可以把一个函数的结果传给另外一个函数,以此实现单线程内程序之间的切换 
    #不用yield:每次函数调用,都需要重复开辟内存空间,即重复创建名称空间,因而开销很大
    import time
    def consumer(item):
        # print('拿到包子%s' %item)
        x=11111111111
        x1=12111111111
        x3=13111111111
        x4=14111111111
        y=22222222222
        z=33333333333
    
        pass
    def producer(target,seq):
        for item in seq:
            target(item) #每次调用函数,会临时产生名称空间,调用结束则释放,循环100000000次,则重复这么多次的创建和释放,开销非常大
    
    start_time=time.time()
    producer(consumer,range(100000000))
    stop_time=time.time()
    print('run time is:%s' %(stop_time-start_time)) #30.132838010787964
    
    
    #使用yield:无需重复开辟内存空间,即重复创建名称空间,因而开销小
    import time
    def init(func):
        def wrapper(*args,**kwargs):
            g=func(*args,**kwargs)
            next(g)
            return g
        return wrapper
    
    @init
    def consumer():
        x=11111111111
        x1=12111111111
        x3=13111111111
        x4=14111111111
        y=22222222222
        z=33333333333
        while True:
            item=yield
            # print('拿到包子%s' %item)
            pass
    def producer(target,seq):
        for item in seq:
            target.send(item) #无需重新创建名称空间,从上一次暂停的位置继续,相比上例,开销小
    
    start_time=time.time()
    producer(consumer(),range(100000000))
    stop_time=time.time()
    print('run time is:%s' %(stop_time-start_time)) #21.882073879241943


    ##################
    缺点

    协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程

    协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程

    协程的定义(满足1,2,3就可称为协程):

    1. 必须在只有一个单线程里实现并发
    2. 修改共享数据不需加锁
    3. 用户程序里自己保存多个控制流的上下文栈
    4. 附加:一个协程遇到IO操作自动切换到其它协程(如何实现检测IO,yield、greenlet都无法实现,就用到了gevent模块(select机制))

    yield切换在没有io的情况下或者没有重复开辟内存空间的操作,对效率没有什么提升,甚至更慢,为此,可以用greenlet来为大家演示这种切换










  • 相关阅读:
    使用npm安装一些包失败了的看过来(npm国内镜像介绍)
    利用JMX统计远程JAVA进程的CPU和Memory
    Spring Boot学习笔记
    django数据库时间存储格式问题
    解决 Ubuntu 无法调节屏幕亮度的问题(转)
    django models auto_now和auto_now_add的区别
    django redis操作
    接口测试的工具
    django中migration文件是干啥的
    mysql简单操作(实时更新)
  • 原文地址:https://www.cnblogs.com/onda/p/7111595.html
Copyright © 2011-2022 走看看