标准方法:

def f(x):
    return 2*x

异步方法:

async def f(x):
    return 2 *x

 

1.协程(不是计算机提供,程序员人为的)

也可以被称为微线程,是一种用户东来内的上下文切换技术。简而言之,其实就是通过一个线程实现代码块相执行。

例如

实现协程的方法:

  • 1. greenlet,早期模块
  • 2. yield关键字
  • 3.asyncio装饰器(py.3.4)
  • 4.async , await关键字(py.3.5)[推荐]

asyncio

import asyncio

@asyncio.coroutine
def func1():
    print(1)
    yield from asyncio.sleep(2)
    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))

 4.python3.5后

import asyncio
async def func1():
    print(1)
    await asyncio.sleep(2)
    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))   #两个函数运行可以随意切换,当这个函数awit等待了,就随机切换到下一个函数

 2.协程意义:

 在一个线程中如果遇到IO等待时间,线程不会傻傻等待,会切换到其它程序执行

  

#普通下载图片,每一张图片下载完成了再开启下一张图片的下载,遇到了io等待时间的话,耗时较长
import requests
def download_image(url):
    print("开始下载:", url)
    #发送网络请求,下载图片
    response = requests.get(url)
    print("下载完成")
    file_name = url.rsplit('_')[-1]
    with open(file_name, mode='wb') as file_object:
        file_object.write(response.content)

if __name__ == '__main__':
    url_list = [
        "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1603213266343&di=b9712f1b1b4b4dacd33b2dec1effa452&imgtype=0&src=http%3A%2F%2Fa0.att.hudong.com%2F56%2F12%2F01300000164151121576126282411.jpg"
        ,
        "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1603213266343&di=58d3e8cd434a928b3aea7271e0b1248a&imgtype=0&src=http%3A%2F%2Fa3.att.hudong.com%2F14%2F75%2F01300000164186121366756803686.jpg"
        ,
        "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1603213266342&di=6147a20ddadd13ba45fa1bb9d4acb064&imgtype=0&src=http%3A%2F%2Fa0.att.hudong.com%2F70%2F91%2F01300000261284122542917592865.jpg"
        ]
    for i in url_list:
        download_image(i)

  协程方式下载

  先发送每一个下载图片的请求,不用等每张图下载好在开始下一个,遇到io等待时间线程就发送图片请求,耗时较短

import aiohttp
import asyncio
async def fetch(session,url):
    print("发送请求:", url)
    #发送网络请求,下载图片
    async with session.get(url, verify_ssl = False) as response:
        content = await response.content.read()
        file_name = url.rsplit('_')[-1]
        with open(file_name, mode='wb') as file_object:
            file_object.write(response.content)
        print("下载完成", url)

async def main():
    async with aiohttp.ClientSession() as session:
        url_list = [
            "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1603213266343&di=b9712f1b1b4b4dacd33b2dec1effa452&imgtype=0&src=http%3A%2F%2Fa0.att.hudong.com%2F56%2F12%2F01300000164151121576126282411.jpg"
            ,
            "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1603213266343&di=58d3e8cd434a928b3aea7271e0b1248a&imgtype=0&src=http%3A%2F%2Fa3.att.hudong.com%2F14%2F75%2F01300000164186121366756803686.jpg"
            ,
            "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1603213266342&di=6147a20ddadd13ba45fa1bb9d4acb064&imgtype=0&src=http%3A%2F%2Fa0.att.hudong.com%2F70%2F91%2F01300000261284122542917592865.jpg"
            ]
        tasks = [ asyncio.create_task(fetch(session,url)) for url in url_list ]
        await asyncio.wait(tasks)

if __name__ =='__main__':
    asyncio.run( main())

3.异步编程

1.事件循环

import asyncio

#去生成或获取一个事件循环
loop = asyncio.get_event_loop()

#将任务放到”人物列表"
loop.run_until_complete(任务)

2.快速上手

  

import asyncio

async def func():
    print("test test1")

result = func()
 
# loop = asyncio.get_event_loop()
# loop.run_until_complete(result)
asyncio.run(result)  #python3.7后的方式

 3.await + 可等待对象(协程对象,Future,Task对象->IO等待)

  实例一.

import asyncio

async def func():
    print("test!")
    response = await asyncio.sleep(2)  #在等待的时间切换去执行其他任务
    print("结束",response)

asyncio.run( func())

  实例二.

import asyncio

async def others():
    print("start")
    await asyncio.sleep(2) 
    print("end")
    return "返回值"

async def func():
    print("执行协程函数内部代码")
    #遇到IO操作挂起当前协程(任务),等IO操作完成之后再继续往下执行
    #当前协程挂起时,事件循环可以去执行其他任务
    response1 = await others()   #等待对象的值得到结果之后再继续往下走
    print("IO请求结束, 结果为:", response1)

    response2 = await others()
    print("IO请求结束, 结果为:", response1)
asyncio.run( func())

执行协程函数内部代码
start
end
IO请求结束, 结果为: 返回值
start
end
IO请求结束, 结果为: 返回值

4.  Task对象
Tasks用于并发调度协程, 通过asyncio.create_task(协程对象)的方式创建Task对象,这样可以让协程加入事件循环

python3.7之前可以用loop.create_task()或ensure_future()函数.

 

import asyncio
from asyncio.tasks import Task
from typing import List, Any

async def others():
    print("start")
    await asyncio.sleep(2)
    print("end")
    return "返回值"

async def func():
    print("func开始")
    task_list: list[Task[Any]] = [
        asyncio.create_task(others()),
        asyncio.create_task(others())
    ]
    print("func结束")

    #当执行某协程遇到IO操作时,会自动切换执行其他任务。
    #此处的await是等待相应的协程全部执行完毕并获取结果
    ret1, ret2 = await asyncio.wait(task_list, timeout = None)

    print(ret1, ret2)
asyncio.run( func())

 5. Future对象

   Task继承Future,Task对象内部await结果的处理基于Future对象来的.

import asyncio
async def set_after(fut):
    await asyncio.sleep(2)
    fut.set_result("666")

async def main():
    #获取当前事件循环
    loop = asyncio.get_running_loop()
    #创建一个任务(future对象)
    fut = loop.create_future()
    #创建一个任务(task对象),绑定了set_after函数,函数内部在2s之后,会给fut赋值
    #即手动设置future任务的最终结果,那么fut就可以结束了
    await loop.create_task(set_after(fut))

    #等待Future对象获取,最终结果,否则一直等下去
    data = await fut
    print(data)

asyncio.run(main())