async await如何实现并发

本文回顾了在JavaScript中使用async/await实现并发的两种方法:通过Promise.all并发读取文件和通过异步操作顺序执行。解释了Promise.all如何实现真正的并发,并分析了不同情况下的并发行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

还记得去年实习参加秋招,二面面试官龙哥(现在的组长)问了一个面试题,async await如何实现并发?今晚突然又想起来,赶紧整理一下。

举例

var fs=require('fs');
var read=function (path) {
    return new Promise((resolve,reject)=>{
            fs.readFile(path,(err,data)=>{
                if(err){
                    reject(err)
                }else{
                    resolve(data)
                }
            })
    })
}
//继发读取
async function ReadTwo(){
    var f1=await read('./a.txt');
    var f2=await read('./b.txt');
}

解决方案

(1) promise.all()

async function readTwo{
	var p=Promise.all([read('./a.txt'),read('./b.txt')]);
	var [f1,f2]=await p
}

因为promise.all是并发,所以这两个请求真正达到并发

(2)没法描述,看代码

async function readTwo{
	var p1=read('./a.txt');
	var p2=read('./a.txt');
	f1=await p1;
	f2=await p2;
}

这两个请求真的是并发,因为它们是异步函数,但是获取结果后的赋值时继发


现在想想好简单,当时都不会。
<think>嗯,用户问的是asyncawait如何实现并发。首先,我需要回忆一下异步编程的基本概念。在Python中,asyncawait是用来处理异步操作的,通常与事件循环配合使用。并发在这里指的是多个任务交替执行,而不是同时执行(并行)。那具体是怎么实现的呢? 我记得,当使用async定义一个协程函数时,这个函数不会立即执行,而是返回一个协程对象。然后需要有一个事件循环来调度这些协程。await关键字用来挂起当前协程,让事件循环去执行其他任务,直到等待的操作完成。这样,多个协程可以在同一个线程中交替运行,实现并发。 不过,用户可能会混淆并发和并行的区别。并发是同时处理多个任务,而并行是同时执行多个任务。在单线程的异步编程中,实现的是并发,通过任务切换来高效利用I/O等待时间。例如,当一个协程遇到I/O操作时,它会释放控制权,让事件循环运行其他协程,这样在等待I/O的过程中,其他任务可以继续执行,提高了程序的效率。 接下来,我需要举一个例子来说明。比如,同时发起多个HTTP请求,使用asyncawait可以让这些请求并发执行。每个请求在发送后等待响应时,事件循环可以处理其他请求,这样总时间不会累积,而是取决于最慢的那个请求的时间。 但是,要注意的是,如果在协程中有阻塞操作(比如同步的I/O或者计算密集型任务),会阻塞整个事件循环,导致并发失效。因此,异步编程需要配合非阻塞的库,比如aiohttp代替requests。 另外,可能需要解释一下事件循环的工作原理。事件循环维护一个任务队列,不断从队列中取出任务执行。当遇到await时,当前任务被挂起,事件循环选择下一个任务执行。当被挂起的任务等待的操作完成(比如I/O就绪),它会被重新放入队列,等待下次调度。 用户可能想知道如何实际编写代码实现并发。比如,使用asyncio.gather来同时运行多个协程。例如,async def main(): await asyncio.gather(task1(), task2()),这样task1和task2会并发执行。 还要注意错误处理,如果一个协程抛出异常,gather会收集异常,需要在调用时处理。此外,设置超时也是常见的需求,可以用asyncio.wait_for来为协程设置超时时间。 总结来说,asyncawait通过协程和事件循环机制,利用非阻塞I/O和任务切换,在单线程中实现并发。这适用于I/O密集型应用,但对CPU密集型任务效果有限,这时候可能需要结合多进程或线程池。 现在,我需要把这些思路整理成用户容易理解的步骤,并给出代码示例,说明如何正确使用asyncawait实现并发。同时,需要指出常见的误区和注意事项,比如避免阻塞调用,使用正确的异步库等。</think>在异步编程中,`async`和`await`通过协程和事件循环实现并发。以下是其工作原理的分步说明: --- ### **1. 协程创建** ```python async def fetch_data(url): # 模拟网络请求 await asyncio.sleep(1) return f"Data from {url}" ``` - 每个`async def`声明一个协程函数 - 调用`fetch_data()`不会立即执行,而是返回协程对象 --- ### **2. 事件循环调度** ```python import asyncio async def main(): # 创建3个并发的网络请求 tasks = [ asyncio.create_task(fetch_data("url1")), asyncio.create_task(fetch_data("url2")), asyncio.create_task(fetch_data("url3")) ] # 等待所有任务完成 await asyncio.gather(*tasks) # 启动事件循环 asyncio.run(main()) ``` - `asyncio.create_task()`将协程包装为任务,加入事件循环队列 - 事件循环按照就绪顺序调度任务执行 --- ### **3. 并发执行流程** ```text 时间轴 Task1 Task2 Task3 ------------------------------------------ 0ms 开始请求 等待调度 等待调度 1ms 遇到await 开始请求 等待调度 2ms 等待响应 遇到await 开始请求 3ms 恢复处理 等待响应 遇到await 4ms 完成✅ 恢复处理 等待响应 5ms 完成✅ 恢复处理 6ms 完成✅ ``` - 总耗时≈3ms(单线程实现并发) - 同步执行需要3&times;3=9ms --- ### **4. 关键技术点** 1. **非阻塞I/O** - 使用异步库(如`aiohttp`代替`requests`) - 所有I/O操作使用`await`挂起 2. **上下文切换** ```python async def example(): print("Start") await asyncio.sleep(1) # 主动让出控制权 print("Resume") # 由事件循环重新调度 ``` 3. **并发模式选择** ```python # 模式1:等待所有任务完成 await asyncio.gather(task1, task2) # 模式2:处理首个完成的任务 done, pending = await asyncio.wait( [task1, task2], return_when=asyncio.FIRST_COMPLETED ) ``` --- ### **5. 常见误区** **错误示例:** ```python async def wrong(): # 同步阻塞操作会破坏并发 time.sleep(1) # ❌ 应该用await asyncio.sleep(1) requests.get(url) # ❌ 应该用aiohttp.ClientSession ``` **正确做法:** ```python async def correct(): async with aiohttp.ClientSession() as session: async with session.get(url) as response: # ✅ return await response.text() ``` --- ### **6. 性能对比** | 请求数量 | 同步方式 | 异步并发 | 提升倍数 | |---------|---------|---------|---------| | 10 | 10s | 1.2s | 8.3x | | 100 | 100s | 1.5s | 66.7x | | 1000 | 1000s | 2.1s | 476x | (假设每次网络请求耗时1秒) --- ### **7. 适用场景** - ✅ I/O密集型:网络请求、数据库操作、文件读写 - ❌ CPU密集型:需配合`run_in_executor`使用线程池 ```python async def cpu_intensive(): loop = asyncio.get_event_loop() await loop.run_in_executor( None, heavy_computation # 将同步计算函数放入线程池 ) ``` 通过合理运用`async/await`,可以在单线程中实现数万并发连接(如使用uvloop优化)。关键是要确保整个调用链都采用异步模式,避免阻塞事件循环。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值