一、定时器
Timer的父类是Thread,所以定时器这里用的是线程
# 多长时间之后执行一个任务 from threading import Timer def task(name): print('我是大帅比--%s'%name) if __name__ == '__main__': # t = Timer(2, task,args=('lqz',)) # 本质是开了个线程,延迟一秒执行 Timer(秒数,函数,arges=一个元组) t = Timer(2, task,kwargs={'name':'lqz'}) # 本质是开了个线程,延迟一秒执行 t.start()
二、协程
协程是为了实现单线程下的并发,属性线程下
协程要解决的问题:保存状态+切换
yield:生成器,只要函数中有yield关键字,这个函数就是生成器,通过yield可以实现保存状态+切换
import time # 串行执行 def func1(): for i in range(100000000): i += 1 def func2(): for i in range(100000000): i += 1 if __name__ == '__main__': ctime = time.time() func1() func2() print(time.time() - ctime) # 7.03256796435 # 通过yield,实现保存状态加切换(自定义的切换,并不是遇到io才切,所有它并不能节约时间) # 单纯的切换,不但不会提高效率,反而会讲低效率 def func1(): for i in range(100000000): i += 1 yield def func2(): g=func1() # 先执行一下func1 for i in range(100000000): i += 1 next(g) # 回到func1执行 if __name__ == '__main__': ctime = time.time() func2() print(time.time() - ctime) #14.764776706695557 # 协程并不是真是存在的某个东西,而是程序员臆想出来的 # 程序员控制,不让自己的程序遇到io,看上去,就实现并发了 ''' 优点如下: 协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级 单线程内就可以实现并发的效果,最大限度地利用cpu
缺点如下: 协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程 协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程
总结协程特点: 必须在只有一个单线程里实现并发 修改共享数据不需加锁 用户程序里自己保存多个控制流的上下文栈(需要保存状态) 附加:一个协程遇到IO操作自动切换到其它协程(如何实现检测IO,yield、greenlet都无法实现,就用到了gevent模块(select机制)) '''
三、greenlet模块
协程相关模块
from greenlet import greenlet import time # 遇到io不会切,初级模块,gevent模块基于它写的,处理io切换 def eat(): print('我吃了一口') time.sleep(1) # p.switch() print('我又吃了一口') # p.switch() def play(): print('我玩了一会') e.switch() print('我又玩了一会') if __name__ == '__main__': e = greenlet(eat) p = greenlet(play) e.switch()
四、gevent模块
协程相关模块
# gevent基于greenlet写的,实现了遇见io自动切换 import gevent import time def eat(name): print('%s 吃了一口' % name) gevent.sleep(1) # io操作 print('%s 又吃了一口' % name) def play(name): print('%s 玩了一会' % name) gevent.sleep(2) print('%s 又玩了一会' % name) if __name__ == '__main__': ctim = time.time() e = gevent.spawn(eat,'lqz') p = gevent.spawn(play,'lqz') e.join() # 等待e执行完成 p.join() print('主') print(time.time() - ctim) #2.0165154933929443 这个程序执行完成,最少需要多长时间 2s多一点 ctim=time.time() eat('lqz') play('lqz') print(time.time()-ctim) # 3.0190377235412598
gevent模块加上猴子补丁
from gevent import monkey;monkey.patch_all()#这句话必须写 import gevent import time def eat(name): print('%s 吃了一口' % name) time.sleep(1) # io操作,被猴子补丁替换之后,gevent.sleep() print('%s 又吃了一口' % name) def play(name): print('%s 玩了一会' % name) time.sleep(2) print('%s 又玩了一会' % name) if __name__ == '__main__': ctim = time.time() e = gevent.spawn(eat,'lqz') p = gevent.spawn(play,'lqz') e.join() # 等待e执行完成 p.join() print('主') print(time.time() - ctim) #2.0165154933929443
五、单线程的套接字并发
使用gevent实现单线程下的套接字并发效果
六、asyncio模块
3.4及其之前版本使用的asyncio模块
import time import asyncio # 把普通函数变成协程函数 # 3.4及其以前这么写,3.5开始就弃用了,3.5版本及其之后用了不报错但会提示已经启用 @asyncio.coroutine def task(): print('开始了') yield from asyncio.sleep(1) #asyncio.sleep(1)模拟io print('结束了') loop=asyncio.get_event_loop() # 获取一个时间循环对象# # 协程函数加括号,并不会真正的去执行,它需要提交给loop,让loop循环着去执行 # 协程函数列表 ctime=time.time() t=[task(),task()] loop.run_until_complete(asyncio.wait(t)) loop.close() print(time.time()-ctime)
现在使用的asyncio模块用法
import time import asyncio from threading import current_thread # 表示我是协程函数,等同于3.5之前的装饰器 async def task(): print('开始了') print(current_thread().name) await asyncio.sleep(3) # await等同于原来的yield from print('结束了') async def task2(): print('开始了') print(current_thread().name) await asyncio.sleep(2) print('结束了') loop=asyncio.get_event_loop() ctime=time.time() t=[task(),task2()] loop.run_until_complete(asyncio.wait(t)) loop.close() print(time.time()-ctime)
---40---