并发编程之协程
1>协程的概念
基于单线程来实现并发,即只用一个主线程(很明显可利用的cpu只有一个)情况下实现并发。
对于单线程下,我们不可避免程序中出现io操作,但如果我们能在自己的程序中控制单线程下的多个任务,
能在一个任务遇到io阻塞时就切换到另外一个任务去计算,这样就保证了该线程能够最大限度地处于就绪态,
由此便引出了协程的概念,协程的本质就是在单线程下,由用户自己控制一个任务遇到io阻塞了就切换
另外一个任务去执行,以此来提升效率。
协程:是单线程下的并发,协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。
与线程并发的区别:
>>:python的线程是属于内核级别的,由操作系统控制调度,如耗时过长或I/O阻塞,就会切换到其他线程执行,
>>:单线程内开启协程,遇到I/O,则由应用程序控制切换,以此来提升效率,
所以总结协程特点如下:
只在单个线程实现并发
修改共享数据不加锁(原理还是切换执行,串行,只是节省了I/O时间,切换之前会保存当前进度,)
应用程序自己控制
2>greenlet模块
greenlet模块可以简单地实现多个任务间的直接切换
如下例子:模拟一个吃饭场景,多数人都喜欢在吃饭的时候玩玩手机,是为‘一边吃饭一边玩手机’,若没有
并发的时候,如下如图左,只能吃完一口,再吃一口(可以把咀嚼过程理解成I/O,因为这个等待这个动作结束才
能进入下一个动作),再玩一下手机,再玩一下这种串行方式进行,
引入协程实现模块greenlet,就可以实现类似于,先送一口饭到嘴里,再一边咀嚼一边玩手机,在等待咀嚼的时候
(I/O),同步玩手机,也就是并发效果了,如下图右,
3>gevent模块
greenlet有个比较明显的缺陷就是:必须清楚的预知程序的I/O点,并在其之前进行任务切换工作,很明显,不太‘智能’。
要解决这个问题,就需要引入另一个更强大的库,Gevent,
Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程。
g1=gevent.spawn(func,1,2,3,x=4,y=5)创建一个协程对象g1,spawn括号内第一个参数是函数名后面可以
有多个参数,可以是位置实参或关键字实参,都是传给函数的
g1.join() # 等待g1结束
gevent.joinall([g1,g2]) # 等待所有协程执行完毕,参数格式list
g1.value # 拿到func1的返回值
解析:如上,单线程下运行了eat和play两个任务,最终耗时约等于最大时长的程序,实现了并发效果。
3.2>优化下
如上gevent.sleep(1)模拟的是gevent可以识别的io阻塞,
而time.sleep(1)或其他的阻塞,gevent是不能直接识别的,这时需要引入mokey方法解决,
from gevent import monkey;monkey.patch_all()必须放到被打补丁者的前面,如time,socket模块之前
或者要用的话,直接放在文件开头就行了。
4>基于协程直线并发的套接字通信
server端
模拟多线程的用户端