Python全栈最全学习之路-网络编程(七)

协程

一、协程概念

1、生成器

def fun(x):
    while True:
        y = (yield) # yield也单独使用,可以加括号,也可以不加
        print(y,'****')
f = fun(1)
next(f)
f.send(123) # 使用生成器可以实现反复的输入和输出
#这里输入和输出可以看成数据间的收发

genertor
通过生成器来实现对函数的反复输入和输出,这样的输入和输出同样可以用来传递数据

2、消费者和生产者模型

import time
def consumer():
    '''消费者,接收数据,处理数据'''
    while True:
        x = yield
        # time.sleep(3) # 这里只是进行了切换,但是并没有节省I/O时间
        print('处理了数据:',x)
def producer():
    '''生产者,产生数据'''
    g = consumer()
    next(g)
    for i in range(20):
        # time.sleep(2)
        g.send(i)
        # 给yield传值,然后再循环给下一个yield传值,并且多了切换的程序
        # 比直接串行执行还多了一些步骤,导致执行效率反而更低了
        print('发送了数据:',i)
# 基于yield保存状态,实现两个任务直接来回切换,即并发的效果
# PS:如果每个任务都加上打印,那么明显地看到两个任务的打印是你一次我一次,即并发执行的
producer() # 在当前线程中只执行了这个函数,但是通过这个函数里面的send切换了另外一个任务

这里的生产者和消费者既不是进程实现,也不是线程实现,而是两个生成器之间相互协同完成的,这种就称为协程

3、协程的概念

3-1、名词解释

协程:是单线程下的并发,又称微线程。
协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。

3-2、优点

1、协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级
2、单线程内就可以实现并发的效果,最大限度地使用CPU

3-3、缺点

1、协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程
2、协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程

3-4、特点

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

二、greenlet

import time
from greenlet import greenlet
def producer():
    for i in range(20):
        print('生产了 %s' % i)
        con.switch(i) # 切换到消费者
        time.sleep(1) # 还是会等待,greenlet只是实现了切换
        print('第%s次生产消费完成' % i)
def consumer():
    while True:
        var = pro.switch() # 等待切换过来和传入值
        print('消费了%s' % var) # 在等待的时候可以先干点其他的事情
con = greenlet(consumer) # 真正的协程由greenlet实现
pro = greenlet(producer)
con.switch() # 消费者进入等待状态

第三方模块,需要安装
pip install greenlet
greenlet
生成器实现协程非常不方便,尤其很多协程的时候,使用greenlet就要方便很多
而这个模块来自python的一个衍生版stackless Python原生的协程(常见是标准Python是cpython),将其协程单独拿出来打包成模块,因此性能要比生成器强很多
注意
这里并没有解决IO阻塞的问题,但是我们使用这个时间来做别的事情了,一般在工作中我们都是进程+线程+协程的方式来实现并发,以达到最好的并发效果

三、gevent

1、gevent实现并发服务器

from gevent import monkey;monkey.patch_all() # 必须放到被打补丁者的前面,如time,socket模块之前,all是指可以所有切换成协程的内容
# 猴子补丁,就是在代码中临时改变对象的属性和方法
import gevent
import socket
server = socket.socket()
server.bind(('127.0.0.1',8989))
server.listen(1000)
def fun_coroutimes(conn):
    while True:
        recv_data = conn.recv(1024)
        if recv_data:
            print(recv_data)
            conn.send(recv_data)
        else:
            conn.close()
            break
while True:
    conn,addr = server.accept()
    # 生成一个协程,并将conn作为参数传入
    gevent.spawn(fun_coroutimes,conn)

gevent
gevent封装了epoll和greenlet,在使用的时候要更加方便
同时实现了IO阻塞时的自动切换

2、网络编程小结

在这里插入图片描述
一切为了让cpu忙起来
如果阻塞在IO,则可以使用epoll
如果一个进程忙计算不过来,对于多核CPU则可以使用多进程,但是开销大,由操作系统调度
如果IO比较密集,则可以使用多线程,因为GIL锁存在,所以都是并发执行,且要注意资源抢占情况,开销适中,由Python解释器调度
协程IO阻塞时,可以自己控制调度到其他的协程,开销最小,使用gevent可以实现IO异步

四、gevent应用

1、实现通信

from gevent import monkey;monkey.patch_all()
from gevent.queue import Queue
import gevent
queue = Queue(3)
def producer(queue):
    for i in range(20):
        print(f'生产了{i}')
        queue.put(i)
def consumer(queue):
    for i in range(20):
        var = queue.get()
        print(f'消费了{var}')
# gebent自动切换,不需要我们使用switch去切换
pro = gevent.spawn(producer,queue)
con = gevent.spawn(consumer,queue)
gevent.joinall([pro,con]) # 等待所有协程结束

2、实现异步

from gevent import spawn,joinall,monkey
monkey.patch_all()
import time
def task(pid):
    time.sleep(0.5) # 模拟阻塞
    print(f'Task {pid} done')
def synchronous():
    for i in range(10):
        task(i)
def asynchronous():
    g_l = [spawn(task,i) for i in range(10)] # 初始化列表
    joinall(g_l) # 等待所有协程执行完
if __name__ == '__main__':
    print('Synchronous:')
    start = time.time()
    synchronous() # 同步方法
    print(f'synchronous used {time.time()-start}')
    print('Asynchronous:')
    start = time.time()
    asynchronous() # 异步方法
    print(f'aynchronous used {time.time()-start}')

在这里插入图片描述
同步:所有的方法都是依次执行,总花费时间是所有方法运行之和
异步:与同步相对应,异步指的是让CPU暂时搁置当前请求的响应,处理下一个请求,当通过轮询或其他方式得到的回调通知后,开始运行。
多任务是将异步放在子任务中完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值