Python学习之路-随笔03 多线程/进程和协程(下篇)
下面属于协程的应用和之前剩下的没写详细的。
asyncio
上篇说过了协程之间切换的开销极小,用这个相对于threading的优势以及业务场景对比还有各种细分的对比能扯很多,我觉得在我目前的阶段不需要太纠结这个
即把这个当成threading的替代即可,asyncio可以简单认为比threading并发量更大,内存开销更小就行了(GIL日常先背个锅)
再说说简单的用法,首先asyncio是一个消息循环,有什么用呢?首先说一下协程工作完一段代码之后要返回主线程,但是这时候可能主线程也在忙,而asyncio相当于
一个信箱,FIFO样式的,协程工作完就把完成通知丢到“信箱”里面去,供主线程有空的时候顺序读取以便安排新的任务。
看代码:
1 import threading 2 import asyncio 3 4 @asyncio.coroutine 5 def t1(): 6 print('去洗菜', threading.currentThread()) 7 yield from asyncio.sleep(5) 8 print('炒菜', threading.currentThread()) 9 10 @asyncio.coroutine 11 def t2(): 12 print('洗米', threading.currentThread()) 13 yield from asyncio.sleep(5) 14 print('煮饭', threading.currentThread()) 15 16 @asyncio.coroutine 17 def t3(): 18 print('洗衣服', threading.currentThread()) 19 yield from asyncio.sleep(5) 20 print('晾衣服', threading.currentThread()) 21 22 loop = asyncio.get_event_loop() 23 tasks = [t1(),t2(),t3()] 24 loop.run_until_complete(asyncio.wait(tasks)) 25 loop.close()
out: 去洗菜 <_MainThread(MainThread, started 7732)> 洗米 <_MainThread(MainThread, started 7732)> 洗衣服 <_MainThread(MainThread, started 7732)> 炒菜 <_MainThread(MainThread, started 7732)> 煮饭 <_MainThread(MainThread, started 7732)> 晾衣服 <_MainThread(MainThread, started 7732)>
@asyncio.coroutine 表面这是一个要扔到消息循环(loop)的协程。asyncio.get_event_loop()是创建一个消息循环,然后分配任务,执行任务并等待任务结束,关闭消息循环。这就是一个简单的用法。
然后为了简化@asyncio.coroutine和yield from,从python3.5开始引入了async and await,使用方法就是用async替换掉@asyncio.coroutine,await替换掉yield from
async def t1(): print('去洗菜', threading.currentThread()) await asyncio.sleep(5) print('炒菜', threading.currentThread())
然后还有诸如绑定回调,协程嵌套等等其他用法暂时参考https://www.cnblogs.com/zhaof/p/8490045.html,不然就等我后面的更新哈哈哈哈哈哈<( ̄︶ ̄)↗
aiohttp
为什么写这玩意儿呢,我隐隐觉得以后会用到它,所以先记下来。
这玩意儿是基于asyncio(就是上面那玩意儿)实现的HTTP框架,可以实现单线程并发IO,主要用于服务器端。毕竟HTTP作为一个IO操作开销也是很大的呀
用之前就先得装好pip install aiohttp或者在ananconda里面点一点安装
真的只是先记下来啊,待我以后再来一篇(¬_¬)
concurrent.futures
一个类似于其他语言的线程池的概念,用的是multiprocessing,真正的并行计算。上面的协程只是伪并行计算。
首先要用到的东西concurrent.futures.Executor,里面有两个
ThreadPoolExecutor和ProcessPoolExecutor,建池子的时候要指定用哪个,然后给里面的max_workers这个参数安排一下数量,给几个核干活。
然后用submit提交任务和任务参数,submit(fn, args, kwargs),然后done执行,result获取结果。
1 from concurrent.futures import ThreadPoolExecutor 2 import time 3 4 5 def task(msg): 6 time.sleep(5) 7 return msg 8 9 10 # 创建一个线程池 11 pool = ThreadPoolExecutor(max_workers=2) 12 13 # 往线程池加入2个task 14 t1 = pool.submit(task, 'hello') 15 t2 = pool.submit(task, 'world') 16 17 print(t1.done()) 18 time.sleep(5) 19 print(t2.done()) 20 21 print(t1.result()) 22 print(t2.result())
然后还有一个判断任务是否结束的问题,可以用as_completed这个方法来实现(用之前要导入= =)
for future in as_completed(all_task): data = future.result()
大概这样用
还有一个map函数,比如给同一个任务分配不同的参数丢进线程池执行
args = ['hello', 'world'] for data in pool.map(task, args): print(data)
这样用呢省去了submit的步骤,然后有一个就是最后的输出顺序是按照参数的顺序来的,而不是执行完成的顺序。