×

C#异步编程面试题

独孤求败 独孤求败 发表于2026-04-30 13:41:24 浏览38 评论0

抢沙发发表评论

适合面试备考、自我检测,覆盖基础到进阶,每道题附详细答案,易懂好记。

基础题(必答,考察核心概念)

1. 什么是C#异步编程?async/await的作用是什么?

答案:异步编程是一种编程模型,用于处理耗时操作(如IO、网络),发起操作后主线程不等待,可继续处理其他任务,操作完成后回调通知,避免程序阻塞、假死。
 async:标记方法为异步方法,允许方法内部使用await;await:等待异步任务完成,不阻塞主线程,释放线程资源,让代码执行逻辑和同步代码一致,简化异步编程。

2. Task和Task的区别是什么?

答案:两者都是异步任务的“载体”,代表未完成的异步操作。
 Task:无返回值的异步任务,适用于不需要返回结果的场景(如异步写入文件);
Task:有返回值的异步任务,适用于需要获取异步操作结果的场景(如异步获取接口数据),通过await可获取泛型T类型的结果。

3. 为什么尽量避免使用async void

答案:async void 是“火并忘”的异步方法,无法捕获异常(异常会直接抛到线程池,导致程序崩溃),也无法等待其完成(不能用await调用),无法获取任务状态。
 唯一适用场景:UI事件处理(如按钮点击),其他场景统一使用Task/Task。

进阶题(考察实战能力)

1. 异步和多线程的区别是什么?什么时候用异步,什么时候用多线程?

答案:核心区别:异步是“编程模型”,不依赖多线程;多线程是“并发执行方式”,是异步的一种实现手段。
 异步:适用于IO密集型操作(网络请求、文件读写、数据库操作),耗时操作期间不占用线程,线程可复用,提升程序响应速度; 
 多线程:适用于CPU密集型操作(如大量计算、循环处理),充分利用多核CPU,让多个任务同时执行,提升计算效率。 最佳实践:IO操作用异步,CPU操作用多线程,两者结合提升程序性能

2. 异步代码中使用.Result或.Wait(),为什么会导致死锁?如何避免?

答案:死锁原因(以UI环境为例):
 1. 主线程调用异步方法,await时会释放主线程,去处理其他UI操作;
 2. 若此时用.Result/.Wait(),会强制阻塞主线程,等待异步任务完成;
 3. 异步任务完成后,需要回到主线程继续执行,但主线程已被阻塞,导致互相等待,形成死锁。
 避免方法:全程使用async/await,不使用.Result/.Wait();若必须同步调用异步方法,可使用Task.Run包装(仅适合非UI环境)。

3. 如何并行执行多个异步任务?Task.WhenAll和Task.WhenAny的区别是什么?

答案:并行执行多个异步任务:先创建多个Task,再用Task.WhenAll或Task.WhenAny等待执行完成。
 Task.WhenAll:等待所有异步任务全部完成后,才继续执行后续代码,返回所有任务的结果,适用于“所有任务都必须完成”的场景(如批量获取数据);
 Task.WhenAny:只要有一个异步任务完成,就继续执行后续代码,返回第一个完成的任务,适用于“超时控制”“只要有一个结果就可用”的场景(如多接口请求,取最快的结果)。

高级题(考察深度理解)

1. ValueTask和Task的区别,什么时候用ValueTask

答案:核心区别:内存分配不同。
  Task:是引用类型,每次创建都会在堆上分配内存,适合异步操作大概率异步完成的场景;
  ValueTask:是值类型,默认在栈上分配内存,无需堆内存分配,适合高频调用、大概率同步完成的场景(如缓存读取、本地数据查询),可提升程序性能。
 注意:ValueTask不能多次await,也不能用于Task.WhenAll/WhenAny,若需要多次使用,可转换为Task(.AsTask())。

2. 如何控制异步任务的并发量?举例说明。

答案:使用SemaphoreSlim(轻量级信号量)控制并发量,限制同时执行的异步任务数量,避免压垮服务或资源耗尽。 示例(限制最多5个并发任务):
图片

3. 异步代码中的异常如何捕获和处理?

答案:异步异常只能在await处捕获,需用try-catch包裹await语句,无法捕获未await的异步任务异常。 示例:
图片
补充:若有多个并行任务,可在await Task.WhenAll后,遍历每个任务的Exception属性,捕获所有任务的异常。


群贤毕至

访客