Python Coroutine Async
先谈谈我了解的语言中有哪些支持协程呢? golang,ruby,lua,python. 那协程作用又是什么呢?用最最最最简单的语言概括:
由我们的程序自己代替系统切换任务,最大可能的让系统认为程序是就绪状态,从而把cpu的执行权限分配给我们的程序。
python 的coroutine
任务切换
yeild + send
我们来看看线程切换都干了些什么?
- cpu执行func1.
- 系统法线func1执行到io操作了,或者计算量小的任务,调低func1的权重.
- 对比权重,发现func2权重现在是最高的了,将func2状态调为准备状态,保存func1上下文。
- 切换到func2上来执行。
参考1 https://www.cnblogs.com/russellyoung/p/python-zhi-xie-cheng.html
import time
def func1():
while True:
print('func1')
yield
def func2():
g = func1()
for i in range(10000000):
i + 1
next(g)
time.sleep(3)
print('func2')
start = time.time()
func2()
stop = time.time()
print(stop - start)
协程
协程就是单线程下的并发(并发、并行),是一种用户级别的轻量级线程,由用户控制调度的微线程。
干嘛用它? 这就要涉及到进程、线程、资源、状态、优先级等综合起来的东西。总的一句话概括就是,你借了系统的钱,系统每过一段时间就来问问你能不能还钱,然后你就一直跟系统打招呼,钱还在股市生娃呢,没控。
用法:
参考2:官方文档https://docs.python.org/3/library/asyncio-task.html
参考1最后的实例非常好
协程、异步
参考3 https://www.cnblogs.com/wangchengpei/p/10958043.html
- eventloop:(qt也有eventloop 狗头.jpg) 循环事件,类似一个循环队列,有事件注册,就会被执行。
- coroutine: 协程
在 Python 中常指代为协程对象类型,我们可以将协程对象注册到时间循环中,它会被事件循环调用。我们可以使用 async 关键字来定义一个方法,这个方法在调用时不会立即被执行,而是返回一个协程对象。
- task:任务,对协程对象的封装,包含了任务的各个状态。
- future:未执行任务。
- async/await:定义协程,阻塞。
eg.1
- 多任务协程:
import asyncio
import requests
import threading
url = 'http://www.cgfantuan.com'
async def request():
status = requests.get(url)
print("Current thread::",threading.current_thread().getName())
return status
tasks = [asyncio.ensure_future(request()) for _ in range(5)]
print("Task::",tasks)
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
for task in tasks:
print("Task result::",task.result())
reslut:
Task:: [<Task pending name='Task-1' coro=<request() running at test02.py:6>>, <Task pending name='Task-2' coro=<request() running at test02.py:6>>, <Task pending name='Task-3' coro=<request() running at test02.py:6>>, <Task pending name='Task-4' coro=<request() running at test02.py:6>>, <Task pending name='Task-5' coro=<request() running attest02.py:6>>]
Current thread:: MainThread
Current thread:: MainThread
Current thread:: MainThread
Current thread:: MainThread
Current thread:: MainThread
Task result:: <Response [200]>
Task result:: <Response [200]>
Task result:: <Response [200]>
Task result:: <Response [200]>
Task result:: <Response [200]>
不管不顾,一顿request,空了慢慢接收response.
eg.2
flask,django,xmlrpc都有以下功能(搬运ing)
# Flask server
from flask import Flask
import time
app = Flask(__name__)
@app.route('/')
def index():
time.sleep(3)
return 'Hello!'
if __name__ == '__main__':
app.run(threaded=True)
import asyncio
import aiohttp
import time
start = time.time()
async def get(url):
session = aiohttp.ClientSession()
response = await session.get(url)
result = await response.text()
await session.close()
return result
async def request():
url = 'http://127.0.0.1:5000'
print('Waiting for', url)
result = await get(url)
print('Get response from', url, 'Result:', result)
tasks = [asyncio.ensure_future(request()) for _ in range(5)]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
end = time.time()
print('Cost time:', end - start)
result:
Waiting for http://127.0.0.1:5000
Waiting for http://127.0.0.1:5000
Waiting for http://127.0.0.1:5000
Waiting for http://127.0.0.1:5000
Waiting for http://127.0.0.1:5000
Get response from http://127.0.0.1:5000 Result: Hello!
Get response from http://127.0.0.1:5000 Result: Hello!
Get response from http://127.0.0.1:5000 Result: Hello!
Get response from http://127.0.0.1:5000 Result: Hello!
Get response from http://127.0.0.1:5000 Result: Hello!
Cost time: 3.0132627487182617
执行过程:
- 按顺序执行task
- 遇到await,挂起当前任务
- 执行其他task,jump step2,无其他task,jump step4
- 桉顺序执行挂起任务
eg.3
# 单进程
import requests
import time
start = time.time()
def request():
url = 'http://127.0.0.1:5000'
print('Waiting for', url)
result = requests.get(url).text
print('Get response from', url, 'Result:', result)
for _ in range(100):
request()
end = time.time()
print('Cost time:', end - start)
# 多进程 + 协程
import asyncio
import aiohttp
import time
from aiomultiprocess import Pool
start = time.time()
async def get(url):
session = aiohttp.ClientSession()
response = await session.get(url)
result = await response.text()
await session.close()
return result
## !!! 有问题,待调试
async def request():
url = 'http://127.0.0.1:5000'
urls = [url for _ in range(5)]
async with Pool() as pool:
result = await pool.map(get, urls)
return result
coroutine = request()
task = asyncio.ensure_future(coroutine)
loop = asyncio.get_event_loop()
loop.run_until_complete(task)
end = time.time()
print('Cost time:', end - start)
参考4 https://segmentfault.com/a/1190000009769387 使用close和throw方法控制协程
参考5 https://github.com/gmarull/asyncqt 这个是pyqt的异步协程,非常好。其实就是把Qt的eventloop给替换了。所以,
QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents)
QCoreApplication.processEvents()
QEventLoop,QRunnable会不会就类似于和携程一样原理的东东(我觉得QRunnable就是携程,没证实,逃)
想法
在项目里面,我这边用了pyqt做前端,所以需要ui和业务分离,而且业务还有调取服务器数据、本地缓存数据,io处理的需求,所以就考虑到需要用到协程。但是!但是!如果是重(四声)计算的task的话,虽然不会卡界面,但是,也会出现问题,毕竟是eventloop,会把task挂起来,所以像GUI界面,大部分不建议用协程。如果你是做网页爬虫的话,那就另当别论了。