碰到 Python 使用的新概念:异步编程 asyncio 库,这个和多线程编程的 threading 有什么区别呢?
特性 | asyncio (异步编程) | threading (多线程编程) |
---|
执行模型 | 事件循环和协程,非阻塞 | 操作系统线程,阻塞 |
资源消耗 | 较低,单线程,适合 I/O 密集型任务 | 较高,多线程,适合 CPU 密集型任务 |
上下文切换 | 由程序控制,协程间切换,开销小 | 由操作系统控制,线程间切换,开销大 |
共享资源和同步 | 通常不需要锁,因为协程在等待 I/O 时会主动让出控制权 | 需要锁来同步共享资源,以避免竞态条件 |
编程模型 | 使用 async 和 await 关键字定义和调用异步函数 | 传统的多线程编程模型,使用 threading. Thread 创建和管理线程 |
错误处理 | 可以使用 try… Except 捕获异常 | 异常处理相对复杂,线程中的异常可能不会直接反映到主线程 |
适用场景 | I/O 密集型和高并发场景,如 Web 服务器、网络爬虫等 | CPU 密集型任务和需要并行处理的场景 |
性能 | 在 I/O 密集型任务中性能通常优于多线程 | 在 CPU 密集型任务中性能可能优于单线程的异步编程 |
这里引入了协程的概念,该如何去理解呢?
特性 | 进程 (Process) | 线程 (Thread) | 协程 (Coroutine) |
---|
定义 | 操作系统进行资源分配和调度的独立单位,应用程序运行的实例。 | 进程中的一个实体,系统独立调度和分派的基本单位。 | 程序组件,允许不同的执行线程暂停和恢复执行。 |
资源 | 拥有独立的内存空间,不共享。 | 共享进程的资源,如内存空间、文件句柄等。 | 共享线程的资源,因为它们运行在同一线程中。 |
开销 | 创建和销毁开销大,涉及系统资源分配和回收。 | 创建和销毁开销相对较小,因为共享进程资源。 | 创建和销毁开销非常小,不需要系统层面的切换。 |
调度 | 由操作系统调度。 | 由操作系统调度。 | 通常由程序自己调度,或由框架 / 库管理。 |
独立性 | 完全独立。 | 半独立,共享进程资源。 | 高度依赖,共享线程资源。 |
适用场景 | 适用于隔离性强的并发任务。 | 适用于资源共享的并发任务。 | 适用于 I/O 密集型的并发任务。 |
层次关系 | 进程包含线程。 | 线程包含协程。 | 协程依附于线程。 |
资源共享 | 进程间不共享资源。 | 线程间共享进程资源。 | |
1 2 3 4 5 6 7 8 9 10 11 12
| import asyncio from datetime import datetime import nest_asyncio nest_asyncio.apply() async def hello(): print('1-',datetime.now().strftime("%H:%M:%S")) await asyncio.sleep(1) print('2-',datetime.now().strftime("%H:%M:%S")) if __name__=='__main__': print('0-',datetime.now().strftime("%H:%M:%S")) asyncio.run(hello()) print('3-',datetime.now().strftime("%H:%M:%S"))
|
0- 08:58:10
1- 08:58:10
2- 08:58:11
3- 08:58:11
1 2 3 4 5 6 7 8 9 10 11 12 13
| import random async def hello(flag:str): sleep_time= random.randint(1,5) print(flag+'-1-',datetime.now().strftime("%H:%M:%S")) print(flag+'-sleep_time:',sleep_time) await asyncio.sleep(sleep_time) print(flag+'-2-',datetime.now().strftime("%H:%M:%S")) async def main(): await asyncio.gather(hello('Thread1'),hello('Thread2')) if __name__=='__main__': print('0-',datetime.now().strftime("%H:%M:%S")) asyncio.run(main()) print('3-',datetime.now().strftime("%H:%M:%S"))
|
0- 09:38:11
Thread1-1- 09:38:11
Thread1-sleep_time: 3
Thread2-1- 09:38:11
Thread2-sleep_time: 5
Thread1-2- 09:38:14
Thread2-2- 09:38:16
3- 09:38:16
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import threading import time import random from datetime import datetime
def hello(flag: str): sleep_time = random.randint(1, 5) print(f"{flag}-1- {datetime.now().strftime('%H:%M:%S')}") print(f"{flag}-sleep_time: {sleep_time}") time.sleep(sleep_time) print(f"{flag}-2- {datetime.now().strftime('%H:%M:%S')}")
def thread_work(flag: str): hello(flag) if __name__=='__main__': thread1 = threading.Thread(target=thread_work, args=("Thread1",)) thread2 = threading.Thread(target=thread_work, args=("Thread2",)) print('0-',datetime.now().strftime("%H:%M:%S")) thread1.start() thread2.start() thread1.join() thread2.join() print('3-',datetime.now().strftime("%H:%M:%S"))
|
0- 09:40:40
Thread1-1- 09:40:40
Thread1-sleep_time: 5
Thread2-1- 09:40:40
Thread2-sleep_time: 2
Thread2-2- 09:40:42
Thread1-2- 09:40:45
3- 09:40:45
从代码运行结果来看,asyncio 与 threading 有相同输出,可以认为:asyncio 一直是单线程,通过在软件层面抽象出协程实现并发