当前位置: 代码网 > it编程>前端脚本>Python > Python协程是什么?如何用asyncio实现高并发异步编程

Python协程是什么?如何用asyncio实现高并发异步编程

2026年09月09日 Python 我要评论
一·协程概念在同一线程内,一段执行代码过程中,可以中断并跳转到另一段代码中,接着之前中断的地方继续执行。协程运行状态于多线程类似。协程优点无需线程上下文切换,协程避免了无意义的调度,可以

一·协程概念

在同一线程内,一段执行代码过程中,可以中断并跳转到另一段代码中,接着之前中断的地方继续执行。

协程运行状态于多线程类似。

协程优点

无需线程上下文切换,协程避免了无意义的调度,可以提高性能 无需原子操作锁定及同步开销。

方便切换控制流,简化编程模型 。

高并发+高扩展性+低成本,一个cpu支持上万的协程不是问题,很适合用干高并发处理

协程缺点

1·无法利用多核资源。协程的本质是单线程,不能同时将单个cpu的多个核用上,协程需要进程配合才能运行在多cpu上。(不过我们日常编程不会有这个问题,除非是cpu密集型应用)

2·进行阻塞操作(如io时)会阻塞掉整个程序

3·通过进程+协程的方式才可以利用多核资源

协程和线程的区别

它们都是“并发”的手段

并发(concurrency)指的是:在同一个时间段内管理多个任务的执行

线程:如果有三个线程对象启动,则会创建三个线程对象,

协程:如果有三个协程函数,但是只会有一个线程

异步的小案例

使用asyncio实现异步程序

import time
import asyncio
now=lambda: time.time()     # 定义 now() 为当前时间戳
start = now()                # 程序开始时间

协程函数
async def fun(i):
    asyncio.sleep(1)
    print(i)
for i in range(5):
    asyncio.run(fun(i))
print(f"当前花费的时间为{now()-start}")       #当前时间 - 开始时间 = 花费的时间

非异步程序

import time
import asyncio
now=lambda: time.time()     # 定义 now() 为当前时间戳
start = now()                # 程序开始时间
def fun_1(i):
    time.sleep(1)
    print(i)
for i in range(5):
    a=fun_1(i)
print(f"当前花费的时间为 {now()-start}")

二·python中asyncio库介绍

asyncio是python3.4之后引入的标准库,内置对异步10的支持。

asyncio的编程模型是一个消息循环,我们从asyncio 模块中直接获取一个eventloop的引用,然后把需要执行的协程扔到eventloop中执行,就实现了异步、

1·事件循环(eventloop)

类似于一个死循环的列表

我们在创建一些协程任务的时候,会将任务放到事件循环中,事件循环会对当前存在的任务进行状态的判断

任务的状态有:已完成,未完成

对于已经完成的任务会进行删除,如果是未完成的任务/等待完成的任务会被事件循环调度

如果当前事件循环的任务已经全部完成,则事件循环列表为空,如果为空,则中断循环并退出。

使用eventloop运行协程函数

一个协程函数,它无法直接运行,需要使用eventloop进行运行,下面是协程函数的两种运行方式

demo1

import asyncio
async def work():
    print("这是一个协程对象")
async def main():
    task=[
        asyncio.create_task(work())
    ]
    await asyncio.wait(task)

asyncio.run(main())
#使用create_task能够运行多个协程对象

demo2

import asyncio
async def work():
    print("这是一个协程对象")
task=[
       work()
    ]

asyncio.run(asyncio,wait(task))

2·coroutine

协程协程对象,指一个使用async 关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。2协程对象需要注册到事件循环,由事件循环调用。

3·task任务

一个协程对象就是一个原生可以挂起的函数,任务则是对协程进一步封装,其中包含任务的各种状态。

task对象

task对象就是往事件循环添加任务的一个接口

task对象就是往事件循环中加入任务用的。task用于开发调度协程,通过asyncio.create task(协程对象)创建(python3.7之后有这个函数),也可以用asyncio.ensure future(corkutine)和 loop.create task(coroutine)创建一个task。

run until complete的参数是一个future对象,当传入一个协程,其内部会自动封装成task。不建议手动实例化task对象

使用task和gather实现协程并发

demo1:使用wait方法循环遍历的方式并发协程对象

async def work(x):
    print(f"当前接收的参数为{x}")

    #模拟一个耗时任务
    await asyncio.sleep(x)
    return f"当前任务的返回值为{x}"
async def main():

    #创建是个任务并提交到事件循环
    tasks=[asyncio.create_task(work(i)) for i in range(10)]

    #tasks是一个列表不能直接await需要使用wait方法迭代列表
    #done 和 pending 是两个集合(set 类型),done包含已经完成的任务,pending包含 仍在等待中、尚未        
    完成的任务
    done, pending=await asyncio.wait(tasks)
    for result in done:
        print(result)

demo2:使用gather的方式并发协程对象

async def work(x):
    print(f"当前接收的参数为{x}")

    #模拟一个耗时任务
    await asyncio.sleep(x)
    return f"当前任务的返回值为{x}"
async def main():

    #创建是个任务并提交到事件循环
    tasks=[asyncio.create_task(work(i)) for i in range(10)

    # gather用来收集所有已完成的任务的返回值,并且获取到的任务返回值是有顺序的
    results=await asyncio.gather(*tasks)
    print(results)
asyncio.run(main())

4·future对象

代表将来执行或者没有执行的任务的结果,它和task没有本质上的区别(task对象其实是future对象的子类 )

5.async / await关键字的作用

1·暂停当前协程,等待另一个协程执行完毕。

2·直接获取另一个协程的返回值。

await关键字的流程和使用

当协程函数(async def)中执行到 await

  • 如果后面的任务 需要等待(比如网络请求、io操作、定时器),

  • 那么当前协程会 挂起(暂停执行),

  • 让出控制权 给事件循环(event loop),

  • 其他任务可以继续执行(比如别的协程或事件处理),

  • 一旦等待的操作完成,事件循环会把这个协程 恢复继续执行

demo

两个协程函数并发执行

python
demo1

import asyncio

async def say_hello(name):
    print(f"{name} 开始")
    await asyncio.sleep(2)
    print(f"{name} 结束")

async def main():
    # 并发启动两个协程任务
    await asyncio.gather(
        say_hello("协程1"),
        say_hello("协程2")
    )

asyncio.run(main())

执行过程解释

  • 协程1协程2 几乎同时启动

  • 遇到 await asyncio.sleep(2) 时,不会阻塞程序,而是让出执行权,事件循环调度另一个协程运行。

  • 两个协程的 sleep并发等待的。

  • 大约 2秒后,两个协程几乎同时结束。

输出如下(2秒内完成):

copyedit
协程1 开始
协程2 开始
协程1 结束
协程2 结束

对比同步版本

如果不使用 asyncawait,而是用同步 time.sleep(4),每个任务会依次等待,总共要 8秒:(因为逻辑上没有使用并发(task对象))

async def other():
    print("this is other")
    await asyncio.sleep(4)
    print("协程任务完成")
    return "这是当前协程任务的返回值"
async def main_1():
    print("process is runing")
    res1=await other()     #它的执行方式是逻辑上的“同步”顺序
    res2=await other()
    print("当前协程任务的返回值为", res1 , res2)
asyncio.run(main_1())

实际执行流程如下:

  1. 打印:process is runing

  2. 调用 other(),打印:this is other

  3. await asyncio.sleep(4) → 程序等待 4秒

  4. 打印:协程任务完成

  5. 返回值赋给 res1

  6. 再次调用 other(),重复上面的步骤 → 又等 4秒

  7. 最后打印两次返回值。

总耗时:大约 8秒

三·协程函数和普通函数的区别

# 普通函数
def foo():
    return 123

# 协程函数
async def bar():
    return 456

foo_obj = foo()     # 返回值:123(已执行)
bar_obj = bar()     # 返回一个协程对象,还没执行!

核心区别在于:「调用后是否立即执行」

类型调用结果是否立即执行可否直接用结果
普通函数得到返回值✅ 已执行✅ 可以立即用
协程函数得到协程对象❌ 未执行❌ 必须 await 或 run

1·协程对象的使用

(1)使用await的时候

从上面我们能够知道,当你在一个协程函数内调用另一个协程函数的时候,直接调用不加await的话不会运行,只会返回一个协程对象

在协程函数内部调用另一个协程函数时,必须用 await,否则被调用的协程函数不会被执行

(2)使用 asyncio.run的时候

直接调用一个协程函数可以不使用await,可以使用asyncio.run() ,因为asyncio.run()设计用于程序的最高层入口点,它会创建新的事件循环并运行你传入的协程,只能在主线程且无事件循环运行时调用

(3)demo1正确使用await返回数据

import asyncio
async def bar():
    numbers="0123456789"
    return numbers
async def bar1():
    numbers="0123456789"
    return numbers
async def bar2():
    numbers="0123456789"
    return numbers
async def main():
    tasks=[asyncio.create_task(bar()),asyncio.create_task(bar1()),asyncio.create_task(bar2())]

    result= await asyncio.gather(*tasks)      
    #asyncio.gather 用于并发运行多个协程,并收集它们的返回值。

    print(result)
if __name__=="__main__":
    asyncio.run(main())       
    #这里是程序的最高点asyncio.run()只能在最高点运行
    #run会自动创建一个eventloop事件循环,当 main() 协程执行完毕后,事件循环会自动关闭,程序结束。

运行结果:正常返回值

(4)demo2不使用await

import asyncio
async def bar():
    numbers="0123456789"
    return numbers
async def bar1():
    numbers="0123456789"
    return numbers
async def bar2():
    numbers="0123456789"
    return numbers
async def main():
    tasks=[asyncio.create_task(bar()),asyncio.create_task(bar1()),asyncio.create_task(bar2())]

    result= asyncio.gather(*tasks)      
    #由于没有使用await所以只会返回协程对象

    print(result)
if __name__=="__main__":
    asyncio.run(main())       
    #这里是程序的最高点asyncio.run()只能在最高点运行
    #run会自动创建一个eventloop事件循环,当 main() 协程执行完毕后,事件循环会自动关闭,程序结束。

由于没有正确使用await,所以会直接返回一个对象而不是对象里的值

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com