入门

'''
1.event_loop: 时间循环,相当于一个无限循环,可以把一些函数注册到这个时间循环上,当满足发生条件
  的时候,就调用相应的处理方法
2.coroutine: 协程对象类型,可以使用async 关键字来定义一个方法,这个方法在调用时不会立即被执行,而是会返回一个协程对象
  我们可以将协程对象注册到事件循环中,它会被事件循环调用,
3.task: 任务,这是协程对象的进一步封装,包含协程对象的各个状态
'''

# 定义一个协成对象
async def execute(x):
    print(f'Number:{x}')

coroutine = execute(1)
print(coroutine)
loop = asyncio.get_event_loop()
task = loop.create_task(coroutine)
print(task)
loop.run_until_complete(task)
print(task)


# out:
'''
<coroutine object execute at 0x000001C1361B3140>
<Task pending name='Task-1' coro=<execute() running at E:\PyProject\asyncioDemo\main.py:31>>
Number:1
<Task finished name='Task-1' coro=<execute() done, defined at E:\PyProject\asyncioDemo\main.py:31> result=None>
'''



# 也可以直接使用 :asyncio.ensure_future(coroutine) 创建task
async def execute(x):
    print(f'Number:{x}')

coroutine = execute(1)
task =  asyncio.ensure_future(coroutine)
loop = asyncio.get_event_loop()
print(task)
loop.run_until_complete(task)
print(task)

绑定回调函数

async def request():
    url = 'http://www.baidu.com'
    status = requests.get(url)
    return status

def callback(task): # 回调函数
    print(f'Status:{task.result()}')

coroutine = request()
task = asyncio.ensure_future(coroutine)
task.add_done_callback(callback)  # 添加回调函数
print(task)

loop = asyncio.get_event_loop()
loop.run_until_complete(task) # 将协程对象注册到事件,并启动
print(task)



# 直接调用task.result() 也可以获得返回结果
async def request():
    url = 'http://www.baidu.com'
    status = requests.get(url)
    return status  # => task.result()
coroutine = request()
task = asyncio.ensure_future(coroutine)

print(task)

loop = asyncio.get_event_loop()
loop.run_until_complete(task) # 将协程对象注册到事件,并启动
print(task.result())
print(task)

多任务协程

多个任务怎么处理?可以创建一个task列表叫给asyncio.awit()

async def request():
    url = 'http://www.baidu.com'
    status = requests.get(url)
    return status  # => task.result()


tasks = [asyncio.ensure_future(request()) for _ in range(1, 5)]
print(tasks)

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

for task in tasks:
    print(f'task result:{task.result()}')


# out:
'''
[<Task pending name='Task-1' coro=<request() running at E:\PyProject\asyncioDemo\main.py:88>>, <Task pending name='Task-2' coro=<request() running at E:\PyProject\asyncioDemo\main.py:88>>, <Task pending name='Task-3' coro=<request() running at E:\PyProject\asyncioDemo\main.py:88>>, <Task pending name='Task-4' coro=<request() running at E:\PyProject\asyncioDemo\main.py:88>>]
task result:<Response [200]>
task result:<Response [200]>
task result:<Response [200]>
task result:<Response [200]>
'''

# 但其实也依然是顺序执行,没有实现异步操作

await关键字

await关键字:它可以将耗时等待的操作挂起,让出控制权。如果协程在执行的时候遇到await,事件循环就会将本协程挂起,转而执行别的协程,直到其他协程挂起或执行完毕

await 后面的对象必须是如下格式之一:

  • 一个原生协程对象
  • 一个由types.coroutine 修饰的生成器,这个生成器返回协程对象
  • 一个包含__await__方法的对象返回的一个迭代器

案例

import requests
import time
import logging
import asyncio


logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(levelname)s: %(message)s')


url = 'https://www.httpbin.org/delay/1'  # 延迟1s
start_time = time.time()
for _ in range(1, 10):
    resp = requests.get(url)
    logging.info(f'scraping {url}')
end_time = time.time()
logging.info(f'total time {start_time - end_time} s')


#out 同步访问耗时 18s:
'''
2022-11-20 18:13:20,426 - INFO: scraping https://www.httpbin.org/delay/1
2022-11-20 18:13:22,395 - INFO: scraping https://www.httpbin.org/delay/1
2022-11-20 18:13:24,697 - INFO: scraping https://www.httpbin.org/delay/1
2022-11-20 18:13:26,675 - INFO: scraping https://www.httpbin.org/delay/1
2022-11-20 18:13:28,680 - INFO: scraping https://www.httpbin.org/delay/1
2022-11-20 18:13:30,728 - INFO: scraping https://www.httpbin.org/delay/1
2022-11-20 18:13:32,905 - INFO: scraping https://www.httpbin.org/delay/1
2022-11-20 18:13:34,886 - INFO: scraping https://www.httpbin.org/delay/1
2022-11-20 18:13:36,980 - INFO: scraping https://www.httpbin.org/delay/1
2022-11-20 18:13:36,980 - INFO: total time -18.95430302619934 s
'''

结合aiohttp 模块实现异步请求,优化上面程序:

import aiohttp

start = time.time()


async def get(url):
    session = aiohttp.ClientSession()
    resp = await session.get(url)
    await resp.text()
    await session.close()
    return resp


async def request():
    url = 'https://www.httpbin.org/delay/1'
    resp = await get(url)

tasks = [asyncio.ensure_future(request()) for _ in range(10)]

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

end = time.time()
print(f'cost time:{end - start}')

# out 完成10个线程总计花费2s:
'''
Waiting for https://www.httpbin.org/delay/1...
Waiting for https://www.httpbin.org/delay/1...
Waiting for https://www.httpbin.org/delay/1...
Waiting for https://www.httpbin.org/delay/1...
Waiting for https://www.httpbin.org/delay/1...
Waiting for https://www.httpbin.org/delay/1...
Waiting for https://www.httpbin.org/delay/1...
Waiting for https://www.httpbin.org/delay/1...
Waiting for https://www.httpbin.org/delay/1...
Waiting for https://www.httpbin.org/delay/1...
ger response<ClientResponse(https://www.httpbin.org/delay/1) [200 OK]>
<CIMultiDictProxy('Date': 'Sun, 20 Nov 2022 10:22:13 GMT', 'Content-Type': 'application/json', 'Content-Length': '370', 'Connection': 'keep-alive', 'Server': 'gunicorn/19.9.0', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true')>

ger response<ClientResponse(https://www.httpbin.org/delay/1) [200 OK]>
<CIMultiDictProxy('Date': 'Sun, 20 Nov 2022 10:22:13 GMT', 'Content-Type': 'application/json', 'Content-Length': '370', 'Connection': 'keep-alive', 'Server': 'gunicorn/19.9.0', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true')>

ger response<ClientResponse(https://www.httpbin.org/delay/1) [200 OK]>
<CIMultiDictProxy('Date': 'Sun, 20 Nov 2022 10:22:13 GMT', 'Content-Type': 'application/json', 'Content-Length': '370', 'Connection': 'keep-alive', 'Server': 'gunicorn/19.9.0', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true')>

ger response<ClientResponse(https://www.httpbin.org/delay/1) [200 OK]>
<CIMultiDictProxy('Date': 'Sun, 20 Nov 2022 10:22:13 GMT', 'Content-Type': 'application/json', 'Content-Length': '370', 'Connection': 'keep-alive', 'Server': 'gunicorn/19.9.0', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true')>

ger response<ClientResponse(https://www.httpbin.org/delay/1) [200 OK]>
<CIMultiDictProxy('Date': 'Sun, 20 Nov 2022 10:22:13 GMT', 'Content-Type': 'application/json', 'Content-Length': '370', 'Connection': 'keep-alive', 'Server': 'gunicorn/19.9.0', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true')>

ger response<ClientResponse(https://www.httpbin.org/delay/1) [200 OK]>
<CIMultiDictProxy('Date': 'Sun, 20 Nov 2022 10:22:13 GMT', 'Content-Type': 'application/json', 'Content-Length': '370', 'Connection': 'keep-alive', 'Server': 'gunicorn/19.9.0', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true')>

ger response<ClientResponse(https://www.httpbin.org/delay/1) [200 OK]>
<CIMultiDictProxy('Date': 'Sun, 20 Nov 2022 10:22:13 GMT', 'Content-Type': 'application/json', 'Content-Length': '370', 'Connection': 'keep-alive', 'Server': 'gunicorn/19.9.0', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true')>

ger response<ClientResponse(https://www.httpbin.org/delay/1) [200 OK]>
<CIMultiDictProxy('Date': 'Sun, 20 Nov 2022 10:22:13 GMT', 'Content-Type': 'application/json', 'Content-Length': '370', 'Connection': 'keep-alive', 'Server': 'gunicorn/19.9.0', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true')>

ger response<ClientResponse(https://www.httpbin.org/delay/1) [200 OK]>
<CIMultiDictProxy('Date': 'Sun, 20 Nov 2022 10:22:13 GMT', 'Content-Type': 'application/json', 'Content-Length': '370', 'Connection': 'keep-alive', 'Server': 'gunicorn/19.9.0', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true')>

ger response<ClientResponse(https://www.httpbin.org/delay/1) [200 OK]>
<CIMultiDictProxy('Date': 'Sun, 20 Nov 2022 10:22:13 GMT', 'Content-Type': 'application/json', 'Content-Length': '370', 'Connection': 'keep-alive', 'Server': 'gunicorn/19.9.0', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true')>

cost time:2.4535582065582275

Process finished with exit code 0

'''

另一种实现协程的方式,python 3.8 已经弃用:

import aiohttp

start = time.time()

# py 3.8 已经弃用
@asyncio.coroutine   # 等价于 async def get(url)
def get(url):
    session = aiohttp.ClientSession()
    resp = yield from session.get(url)   # 等价于 await session.get(url)
    yield from resp.text()
    yield from session.close()
    return resp

@asyncio.coroutine
def request():
    url = 'https://www.httpbin.org/delay/1'
    print(f'Waiting for {url}...')
    resp = yield from get(url)
    print(f'ger response{resp}')
tasks = [asyncio.ensure_future(request()) for _ in range(10)]

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

end = time.time()

print(f'cost time:{end - start}')