协程

协程

协程的概念

协程,又称微线程。线程是系统级别的它们由操作系统调度,而协程则是程序级别的由程序根据需要自己调度。在一个线程中会有很多函数,我们把这些函数称为子程序,在子程序执行过程中可以中断去执行别的子程序,而别的子程序也可以中断回来继续执行之前的子程序,这个过程就称为协程。
协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。

yield实现协程

yield表达式有一个返回值,send方法的作用就是控制这个返回值,send的参数就是yield表达式的返回值。
生产者-消费者模型

def constomer():
    r = ''
    while True:
        n = yield r
        print(f'Constomer has take away {n}')
        if not n:
            break

def producer(c):
    c.send(None) #启动生成器
    n = 0
    while n < 5:
        n += 1
        print(f'Producer starting {n}')
        r = c.send(n)
    c.close()
    
if __name__ == '__main__':
    c = constomer()
    producer(c)

yield from 两个的应用

1.拼接可迭代对象

astr = 'abc'
alist = [1,2,3]
adict = {"name":"anthony","age":"24"}
agen = (i for i in range(1,10,2))
def gen(*args):
    for item in args:
        yield from item

new_list = gen(astr,alist,adict,agen)
print(list(new_list))

2.生成器的嵌套
在这里插入图片描述
调用方:调用委派生成器的客户端(调用方)代码
委托生成器:包含yield from表达式的生成器函数;在调用方与子生成器之间建立一个双向通道.
子生成器:yield from后面加的生成器函数

#子生成器
def generator_1():
    total = 0
    average = 0
    count = 0
    while True:
        num = yield average
        if num is None:
            break
        count += 1
        total += num
        average = total/count
    return total,count,average

#委托生成器
def proxy_gen():
    while True:
        #当子生成器结束了,yield from 左边的变量才会被赋值
        total,count,average = yield from generator_1()
        print(f'计算完毕!\n 总共传入{count},总和{total},平均值为{average}')

#调用方
def main():
    calc_average = proxy_gen()
    #启动生成器
    next(calc_average)
    print(calc_average.send(10.0))
    print(calc_average.send(20.0))
    print(calc_average.send(30.0))
    #结束协程
    calc_average.send(None)

if __name__ == '__main__':
    main()

greenlet 实现协程

greenlet可以实现一个自行调度的微线程

from greenlet import greenlet
import time


def task1():
    while True:
        print('正在执行task1')
        time.sleep(0.5)  # 模拟堵塞
        g2.switch() # 遇堵塞,切换到第二个任务


def task2():
    while True:
        print('正在执行task2')
        time.sleep(0.5)  # 模拟堵塞
        g1.switch() #遇堵塞,切换到第一个任务



if __name__ == '__main__':
    #创建greenlet对象
    g1 = greenlet(task1)
    g2 = greenlet(task2)
    g1.switch() #开始任务

gevent 实现协程

能够自动识别程序中的耗时操作,在耗时的时候自动切换到其他任务

from gevent import monkey
import gevent
import time

monkey.patch_all()

def task1():
    while True:
        print('正在执行task1')
        time.sleep(0.5)
        # gevent.sleep(0.5)

def task2():
    while True:
        print('正在执行task2')
        time.sleep(0.5)

if __name__ == '__main__':
    # 遇到延迟才切换  如果没有遇到延迟的话 就不切换
    #创建gevent对象 gevent.spawn(函数名,参数)
    # g1 = gevent.spawn(task1)
    # g2 = gevent.spawn(task2)
    # #等主线程等待协程结束后退出
    # g1.join()
    # g2.join()
    gevent.joinall(
        [
            gevent.spawn(task1),
            gevent.spawn(task2)
        ]
    )

IO模型

什么是IO?

我们都知道unix世界里、一切皆文件、而文件是什么呢?文件就是一串二进制流而已、不管socket、还是FIFO、管道、终端、对我们来说、一切都是文件、一切都是流、在信息交换的过程中、我们都是对这些流进行数据的收发操作、简称为I/O操作。在 I/O 设备上的输入和输出被处理为对相应的文件的读写操作。当打开一个文件时,内核会返回一个非负整数,用来标识该文件,称为文件描述符,简称fd。在此后的读、写等处理过程中,应用程序即可通过这个描述符来访问文件,而不需要记录有关文件的其他信息。
在网络编程中常用的套接字( socket )也是一种文件类型,一个套接字便是一个有着对应描述符的打开的文件,它用于和另外一个进程进行网络通信。

IO交互

在这里插入图片描述

在这里插入图片描述
内核空间中存放的是内核代码和数据、而进程的用户空间中存放的是用户程序的代码和数据、不管是内核空间还是用户空间、它们都处于虚拟空间中、Linux使用两级保护机制:0级供内核使用、3级供用户程序使用。
操作系统和驱动程序运行在内核空间、应用程序运行在用户空间、两者不能简单地使用指针传递数据、因为Linux使用的虚拟内存机制、其必须通过系统调用请求kernel来协助完成IO动作、内核会为每个IO设备维护一个缓冲区、用户空间的数据可能被换出、当内核空 间使用用户空间指针时、对应的数据可能不在内存中。
对于一个输入操作来说、进程IO系统调用后、内核会先看缓冲区中有没有相应的缓存数据、没有的话再到设备中读取、因为设备IO一般速度较慢、需要等待、内核缓冲区有数据则直接复制到进程空间。

所以、对于一个网络输入操作通常包括两个不同阶段:
(1)等待网络数据到达网卡 -----读取到内核缓冲区
(2)从内核缓冲区复制数据 ----- 用户空间

IO模型

常用模型分为5种:

1.阻塞IO

在这里插入图片描述
在内核将数据准备好之前,系统调用会一直等待所有的套接字,默认的是阻塞方式。

2.非阻塞IO

在这里插入图片描述
每次客户询问内核是否有数据准备好,即文件描述符缓冲区是否就绪。当有数据报准备好时,就进行拷贝数据报的操作。当没有数据报准备好时,也不阻塞程序,内核直接返回未准备就绪的信号,等待用户程序的下一个轮寻。

但是,轮寻对于CPU来说是较大的浪费,一般只有在特定的场景下才使用。

3.信号驱动IO

在这里插入图片描述
信号驱动IO模型,应用进程告诉内核:当数据报准备好的时候,给我发送一个信号,对SIGIO信号进行捕捉,并且调用我的信号处理函数来获取数据报。

4.IO多路复用

在这里插入图片描述
IO多路转接是多了一个select函数,select函数有一个参数是文件描述符集合,对这些文件描述符进行循环监听,当某个文件描述符就绪时,就对这个文件描述符进行处理。

其中,select只负责等,recvfrom只负责拷贝。
IO多路转接是属于阻塞IO,但可以对多个文件描述符进行阻塞监听,所以效率较阻塞IO的高。

5.异步IO

在这里插入图片描述

当应用程序调用aio_read时,内核一方面去取数据报内容返回,另一方面将程序控制权还给应用进程,应用进程继续处理其他事情,是一种非阻塞的状态。
当内核中有数据报就绪时,由内核将数据报拷贝到应用程序中,返回aio_read中定义好的函数处理程序。

IO模型对比

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值