多线程和多进程:
优点:可以为阻塞操作提供异步执行
缺点:无法无限制的创建线程

进程池和线程池:
好处:可以系统对进程和线程的创建和销毁的频率,从而降低系统的开销
缺点:线程池和进程池是固定的。有上限。


线程池的基本使用
# 导入进程池的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())