创新互联专业为企业提供老边网站建设、老边做网站、老边网站设计、老边网站制作等企业网站建设、网页设计与制作、老边企业网站模板建站服务,10余年老边做网站经验,不只是建网站,更提供有价值的思路和整体网络服务。
目录
asyncio.1
事件循环:...1
协程:...2
aiohttp:...4
异步io,3.4version加入标准库;
asyncio底层基于selectors实现,看似库,其实是个框架,包含异步IO、事件循环、协程、任务等内容;
例:
def a():
for x in range(3):
time.sleep(0.001) #生产上不能加此句,仅在多线程下做实验用
print('a.x', x)
def b():
for x in 'abc':
time.sleep(0.001)
print('b.x', x)
# a() #串行,两件事如果有因果关系才需要串行,否则并行要好
# b()
# threading.Thread(target=a).start() #多线程下,由OS控制;输出会乱
# threading.Thread(target=b).start()
if __name__ == '__main__':
multiprocessing.Process(target=a).start() #真正的同时进行,由OS控制
multiprocessing.Process(target=b).start()
是asyncio提供的核心运行机制;
loop = asyncio.get_event_loop() #返回一个事件循环对象,是asyncio.BaseEventLoop的实例;
loop.stop()、asyncio.AbstractEventLoop.stop() #停止运行事件循环
loop.run_forever()、asyncio.AbstractEventLoop.run_forever() #一直运行,直到stop
loop.run_until_complete(future)、asyncio.AbstractEventLoop.run_until_complete(future) #运行直至Future对象运行完
loop.close()、asyncio.AbstractEventLoop.close() #关闭事件循环
loop.is_running()、asyncio.AbstractEventLoop.is_running() #返回事件循环是否运行
例:
协程,有返回值None,但没有用;
由自己控制(类似于并发的控制),与多线程、多进程没关系;
在线程内通过生成器完成了调度,让两个函数几乎都有在执行,这样的调度不是OS的进程、线程完成的,而是用户自己设计的;
编写此程序,要使用yield来让出控制权,要用循环帮助执行;
def a():
for x in range(3):
time.sleep(0.001)
print('a.x', x)
yield
def b():
for x in 'abc':
time.sleep(0.001)
print('b.x', x)
yield
m = a()
n = b()
for _ in range(3):
next(m)
next(n)
不是进程,也不是线程,它是用户空间调度完成并发处理的方式;
进程、线程由OS完成调度,而协程是线程内完成调度,它不需要更多的线程,自然也没有多线程切换带来的开销;
协程是非抢占式调度(串行),只有一个协程主动让出控制权,另一个协程才会被调度;
协程也需要使用锁机制,因为是在同一个线程中执行;
多cpu下,可使用多进程+协程配合,既能进程并发又能发挥协程在单线程中的优势;
py中协程是基于生成器的;
3.5version开始,py提供关键字async、await,在语言上原生支持协程,支持async def、async with、async for;
async def用来定义协程函数,调用后即是协程对象(同生成器函数、生成器对象),协程函数中可以不包含await、async关键字,不能使用yield关键字;
asyncio.iscoroutinefunction(sleep) #判断是否是协程函数
asyncio.iscoroutine(thread) #判断是否是协程对象
例:
@asyncio.coroutine #使用装饰器,3.4ver用法
def sleep(x): #生成器+函数(函数中有yield语句即为生成器函数)
for i in range(3): #有循环
print('sleep {}'.format(i))
yield from asyncio.sleep(x) #asyncio.sleep(x),另一个生成器对象
loop = asyncio.get_event_loop()
loop.run_until_complete(sleep(10)) #sleep(3)必须要有括号,sleep为生成器函数,可理解为拿到生成器对象,用返回的对象循环
loop.close()
例:
asyncdef sleep(x): #3.5ver用法
for i in range(3):
print('sleep {}'.format(i))
await asyncio.sleep(x)
async def showthread(x):
for i in range(3):
print(threading.enumerate())
await asyncio.sleep(x)
loop = asyncio.get_event_loop()
tasks = [sleep(3), showthread(3)] #放协程对象
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
print(asyncio.iscoroutinefunction(sleep))
print(asyncio.iscoroutine(showthread))
输出:
[<_MainThread(MainThread, started 19172)>]
sleep 0
[<_MainThread(MainThread, started 19172)>]
sleep 1
[<_MainThread(MainThread, started 19172)>]
sleep 2
True
False
例:
TcpEchoServer
>pip install aiohttp
对于socket,在accept()后,关键是recv()和send();
对于http,关键是request和response;
异步的好处:
没有多线程,并发用多进程;
多进程 + 协程,可完成很高的并发;
简单函数-->多线程-->IO复用-->异步io;
例,服务端:
from aiohttp import web
async def indexhandle(request: web.Request):
return web.Response(text=request.path, status=201)
async def handle(request: web.Request):
print(request.match_info)
print(request.query_string)
return web.Response(status=200,text=request.match_info.get('id', '0000'))
app = web.Application()
app.router.add_get('/', indexhandle)
app.router.add_get('/{id}', handle)
web.run_app(app, host='0.0.0.0', port=9999)
输出:
======== Running on http://0.0.0.0:9999 ========
(Press CTRL+C to quit)
例,客户端:
from aiohttp import ClientSession
import asyncio
async def get_html(url: str):
async with ClientSession() as session: #client与server建立会话
async with session.get(url) as res: #response
print(res.status)
print(await res.text())
url = 'http://127.0.0.1:9999/555' #服务端
loop = asyncio.get_event_loop()
loop.run_until_complete(get_html(url)) #爬虫的第一步,拿到html页面数据后,用第三方库解析html变成dom树,拿到想要的东西
loop.close()
输出:
200
555