多线程和多进程:
优点:可以为阻塞操作提供异步执行
缺点:无法无限制的创建线程
进程池和线程池:
好处:可以系统对进程和线程的创建和销毁的频率,从而降低系统的开销
缺点:线程池和进程池是固定的。有上限。
线程池的基本使用
# 导入进程池的pool
from multiprocessing.dummy import Pool
# 实例化线程对象
pool = Pool(4)
# map func iterator chunksize
pool.map(get_page,name_list)
协程:
单线程+异步协程(推介)
evelent_loop: 事件循环,相当于一个无限循环,可以把一些函数注册到这个循环中
coroutine:协程对象,我们可以把协程对象注册到事件循环中
task:任务,他是对协程对象的进一步封装,包含了任务的每个状态
future;代表将来执行或还没有执行的任务,task和future没有本质区别
async: 定义一个协程
await:挂起阻塞方法的执行
import asyncio
# 提供了async await 两个关键字
async def requests1(url):
print('正在请求的url',url)
print('请求成功',url)
# async 修饰的函数,调用之后返回的是一个协程对象
c = requests1('http://www.baidu.com')
print(c)
# 创建一个事件循环对象
loop = asyncio.get_event_loop()
# 将协程对象注册到loop中,然后启动loop c 是协程对象
loop.run_until_complete(c)
# task 的使用
loop = asyncio.get_event_loop()
task = loop.create_task(c) # 将协程对象 传入loop ,loop创建一个task对象
print(task) # 打印任务对象,正在执行,协程状态
# 将任务对象注册到loop
loop.run_until_complete(task)
print(task) # 打印任务对象,表示任务已经完成
# future
loop = asyncio.get_event_loop()
# 创建future对象
task = asyncio.ensure_future(c) # 任务对象
print(task)
loop.run_until_complete(task) # 注册
print(task)
绑定回调函数
import asyncio
# 提供了async await 两个关键字
async def requests1(url):
print('正在请求的url',url)
print('请求成功',url)
return url
# async 修饰的函数,调用之后返回的是一个协程对象
c = requests1('http://www.baidu.com')
print(c)
# 绑定回调
def callback_func(task):
print(task.result()) # task.result() 返回任务对象的返回值
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(c)
# 当任务对象执行成功之后,执行回调函数 , 将回调函数绑定到任务对象
task.add_done_callback(callback_func) # 默认将任务对象作为参数传递给回调函数
loop.run_until_complete(task)
多任务异步协程实现
import asyncio
import time
async def request(url):
print('正在下载:',url)
time.sleep(3)
print("下载完成",url)
urls = [
'https://www.baidu.com',
'https://www.sougou.com',
'https://www.xinlang.com',
]
start = time.time()
# 任务列表,需要粗放多个任务对象
tasks = []
for url in urls:
c = request(url) # 协程对象
task = asyncio.ensure_future(c) # 任务对象
tasks.append(task)
loop = asyncio.get_event_loop() # 事件循环
loop.run_until_complete(asyncio.wait(tasks)) # 注册
print(time.time()-start) # 9秒, 没有节省时间
原因出在哪里?
time.sleep(3)
在异步协程中,如果出现同步模块相关的代码,那么就无法实现异步。
解决
asyncio.sleep(3)
当在asyncio中遇到阻塞操作必须进行手动挂起
await
import asyncio
import time
async def request(url):
print('正在下载:',url)
# time.sleep(3)
await asyncio.sleep(3)
print("下载完成",url)
urls = [
'https://www.baidu.com',
'https://www.sougou.com',
'https://www.xinlang.com',
]
start = time.time()
# 任务列表,需要粗放多个任务对象
tasks = []
for url in urls:
c = request(url) # 协程对象
task = asyncio.ensure_future(c) # 任务对象
tasks.append(task)
loop = asyncio.get_event_loop() # 事件循环
loop.run_until_complete(asyncio.wait(tasks)) # 注册
print(time.time()-start) # 3.003420114517212秒,
aiohttp+多任务异步协程
import asyncio
import aiohttp
import requests
import time
urls = [
'https://www.baidu.com',
'https://www.sougou.com',
'https://www.bilibili.com/',
'https://www.bilibili.com/',
'https://www.bilibili.com/',
'https://www.bilibili.com/',
'https://www.bilibili.com/',
'https://www.bilibili.com/',
'https://www.bilibili.com/',
]
async def get_page(url):
print("正在下载", url)
res = requests.get(url) # 基于同步代码 , 必须使用基于异步的网络请求模块
print(res.status_code)
print("下载外币",url)
start = time.time()
tasks = []
for url in urls:
c = get_page(url)
task = asyncio.ensure_future(c)
tasks.append(task)
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
print(time.time()-start) #2.431379795074463
aiohttp:
pip install aiohttp
使用该模块的clientsesession
import asyncio
import aiohttp
import requests
import time
urls = [
'https://www.baidu.com',
'https://www.sougou.com',
'https://www.bilibili.com/',
'https://www.bilibili.com/',
'https://www.bilibili.com/',
'https://www.bilibili.com/',
'https://www.bilibili.com/',
'https://www.bilibili.com/',
'https://www.bilibili.com/',
]
async def get_page(url):
print("正在下载", url)
# res = requests.get(url) # 基于同步代码 , 必须使用基于异步的网络请求模块
# print(res.status_code)
async with aiohttp.ClientSession() as session:
async with await session.get(url) as res :
# text() 返回字符串数据
# read() 返回二进制数据
# json() 返回json对象
# 注意获取响应数据之前,一定要使用await手动挂起
page_text = await res.text(encoding='utf8')
print(page_text)
print("下载外币",url)
start = time.time()
tasks = []
for url in urls:
c = get_page(url)
task = asyncio.ensure_future(c)
tasks.append(task)
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
print(time.time()-start) # 1.3533799648284912
# RuntimeWarning: coroutine 'ClientResponse.text' was never awaited
# self._context.run(self._callback, *self._args)
# 注意获取响应数据之前,一定要使用await手动挂起
aiohttp
get ,post
ua伪装
get
headers paramas
post
headers data , proxy 代理ip不是字典,而是字符串http://ip:port
协程:
实现方式:
1:greenlet 早期模块
2:yield关键字
3:asyncio装饰器 p3.4
4:async await p3.5
greenlet 实现协程
from greenlet import greenlet
def func1():
print(1)
gr2.switch()
print(2)
gr2.switch()
def func2():
print(3)
gr1.switch()
print(4)
gr1 = greenlet(func1)
gr2 = greenlet(func2)
gr1.switch() # 第一步,执行func1
# 1
# 3
# 2
# 4
yield 实现协程
# yield
def func1():
yield 1
yield from func2()
yield 2
def func2():
yield 3
yield 4
f1 = func1()
for item in f1:
print(item)
# 1
# 3
# 4
# 2
asyncio 遇到io操作自动切换
coroutine过期了
import asyncio
@asyncio.coroutine
def func1():
print(1)
yield from asyncio.sleep(1)
print(2)
@asyncio.coroutine
def func2():
print(3)
yield from asyncio.sleep(2)
print(4)
tasks = [
asyncio.ensure_future(func1()),
asyncio.ensure_future(func2()),
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
import asyncio
async def func1():
print(1)
await asyncio.sleep(1)
print(2)
async def func2():
print(3)
await asyncio.sleep(2)
print(4)
tasks = [
asyncio.ensure_future(func1()),
asyncio.ensure_future(func2()),
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
协程意义:
在一个线程中如果遇到io等待的时间,线程不会傻傻等,利用空闲的时间再去干点其他事。
事件循环:理解为一个死循环,去检测
伪代码
任务列表
whle true :
可执行的任务列表,已完成的任务列表=去任务列表检查所有的任务,将可执行和已完成任务返回
for 就绪任务 in 可执行的任务列表
执行已就绪的任务
for 已完成的任务 in 已完成的任务列表
任务列表中移除 已完成的任务
如果任务列表中的任务已完成 终止循环
import asyncio
生成一个事件循环
loop = asyncio.get
将事件放到事件循环
loop.run
快速上手
协程函数:定义函数的时候async def
协程对象,执行协程函数的得到的协程对象
协程函数
async def func():
pass
result = func() 得到协程对象
await
可等待对象:
协程对象, future , task 对象====》目前裂解成io等待
import asyncio
async def func():
res = await asyncio.sleep(1)
return res
async.run(func())
import asyncio
async def others():
print('start')
await asyncio.sleep(2)
print('end')
return '返回值'
async def func():
print('111')
res = await others()
print('2222',res)
res = await others()
print('333',res)
asyncio.run(func())
一个协程函数可以有多个await
task对象:
干什么?
在事件循环中并发添加多个任务。
asyncio.create_task()方式创建多个task任务。
这样可以将协程加入事件循环中
还可以:
loop.create_task() before p37
looop.enture_future()
asyncio.create_task() --python3.7 引入的
async def others():
print('start')
await asyncio.sleep(2)
print('end')
return '返回值'
async def func():
print('111')
# 创建任务,将协程对象添加到事件循环中
task1 = asyncio.create_task(others())
task2 = asyncio.create_task(others())
print('任务创建成功')
# 当执行某协程遇到io操作时,会自动切换执行其他任务
# await是等待相对应的协程全部执行完毕再获取结果
ret1 = await task1
ret2 = await task2
print(ret1,ret2)
asyncio.run(func())
# 111
# 任务创建成功
# start
# start
# end
# end
# 返回值 返回值
async def others():
print('start')
await asyncio.sleep(2)
print('end')
return '返回值'
task_list = [
others(),
others()
]
done ,pending = asyncio.run(
asyncio.wait(
task_list
)
)
future对象:
更底层:
task类的基类
task继承future对象,task内部await结果的处理基于future对象
理解为await等待处理
async def main():
loop = asyncio.get_running_loop()
fut = loop.create_future()
await fut
asyncio.run(main())
async def func(fut):
print(1)
await asyncio.sleep(2)
fut.set_result('555')
async def main():
loop = asyncio.get_running_loop()
fut = loop.create_future()
await loop.create_task(func(fut))
data = await fut
print(data)
asyncio.run(main())
# 1
# 555
# sys:1: RuntimeWarning: coroutine 'others' was never awaited
concurrent.futures.Future对象
使用线程池和进程池实现异步操作
from concurrent.futures import Future
from concurrent.futures.thread import ThreadPoolExecutor
from concurrent.futures.process import ProcessPoolExecutor
import time
def func(value):
time.sleep(2)
# print(value)
return value
pool = ThreadPoolExecutor(max_workers=5)
for i in range(10):
fut = pool.submit(func,i)
print(fut)
协程和进程池交叉使用
比如
mysql不支持asyncio
有的第三方模块不支持协程
import time
import asyncio
import concurrent.futures
def func1():
time.sleep(1)
return 'sa'
async def main():
loop = asyncio.get_running_loop()
# 在普通函数转为future对象
fut = loop.run_in_executor(None,func1)
print(fut)
res = await fut
print('res',res)
asyncio.run(main())
<Future pending cb=[_chain_future.<locals>._call_check_cancel() at /Users/lizejun/.pyenv/versions/3.8.0/lib/python3.8/asyncio/futures.py:357]>
res sa
什么时候使用
在编程异步的时候,如果遇到第三方模块不支持异步。
案例:asyncio+不支持异步的模块
import asyncio
import requests
async def download(url):
print('开始下载',url)
loop = asyncio.get_running_loop()
# request不支持异步,使用线程池配合实现异步
future = loop.run_in_executor(None,requests.get,url)
res = await future
print('下载完成')
import random
filename = url.split('_')[-1]
if len(filename) > 20:
filename = str(random.randint(1,1000))+".jpg"
with open(filename,mode='wb') as file_obj:
file_obj.write(res.content)
if __name__ == '__main__':
url_list = [
"https://ns-strategy.cdn.bcebos.com/ns-strategy/upload/fc_big_pic/part-00023-1899.jpg",
"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1608623433169&di=2890da7609ad80683a8e642ae2f91a1c&imgtype=0&src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fq_70%2Cc_zoom%2Cw_640%2Fimages%2F20180815%2Fc80db8ae0d044eafb020d6e91e6151f4.jpeg",
"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1608623351187&di=ad5c0c3592dff8bdc0130634867b9a98&imgtype=0&src=http%3A%2F%2Fdingyue.ws.126.net%2FfMgAJpgFotLL5HnpJIGZvZukGgSwp1flGaDMD4yLT2Eps1552963002835.jpg",
]
tasks = [download(url) for url in url_list]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
异步迭代器:
实现了__aiter__ __anext__()方法的对象,
__aiter__ 必须返回一个awaitable对象
async_for 会处理异步迭代器的__anext__方法返回的可等待对象,
直到引发一个stopasynciteration异常
异步迭代对象:
可以在async_for语句中被使用的对象,,必须通过他的__aiter方法返回一个asyncthors_iterator
import asyncio
class Reader(object):
'''自定义异步迭代器(同时也是异步可迭代对象)'''
def __init__(self):
self.count = 0
async def readline(self):
self.count += 1
if self.count == 100:
return None
return self.count
def __aiter__(self):
return self
async def __anext__(self):
val = await self.readline()
if val == None:
raise StopAsyncIteration
return val
#
# obj = Reader()
# async for item in obj:
# print(item)
#
# async for item in obj:
# ^
# SyntaxError: 'async for' outside async function
# async for 必须写在协程函数内
async def func():
asyncio 异步上下文管理器
这种对象通过定义aenter和aexit 方法对async with 语句中的环境进行控制
import asyncio
class AsyncCOntextManager:
def __init__(self):
self.conn = ''
async def do_sometiong(self):
pass
async def __aenter__(self):
pass
async def __aexit__(self, exc_type, exc_val, exc_tb):
pass
async def func():
async with AsyncCOntextManager() as f:
res = await f.do_sometiong()
print(res)
if __name__ == '__main__':
asyncio.run(func())