如何实现调用外部C# DLL的异步Action方法?是否有必要?
改造异步方法的步骤及必要性分析
一、如何改造为异步方法
改造分为两种情况,核心取决于外部DLL是否提供原生异步API:
情况1:外部DLL已有异步版计算方法
如果IClacSdk已经提供了异步方法(比如命名为CalcAsync),直接修改Action即可:
[HttpPost] public async Task<IActionResult> SetCalcAsync([FromBody] CalcRequest calcRequest) { try { IClacSdk clacSdk = new CalcSdk(); // 使用await调用异步方法 ClacResult clacResult = await clacSdk.CalcAsync(calcRequest.id, calcRequest.username); CustomeResponse customeResponse = new CustomeResponse() { ErrorDescription = clacResult.ErrorDescription, ErrorNum = clacResult.ErrorNum, ClacResult = clacResult.Result }; return Ok(customeResponse); } catch (Exception exc) { return StatusCode(500, GetErrorFormat(exc)); } }
关键改动点:
- 方法返回值改为
Task<IActionResult> - 添加
async关键字 - 方法名遵循ASP.NET惯例加上
Async(可选但推荐) - 用
await替代直接调用同步方法
情况2:外部DLL只有同步计算方法
如果DLL没有提供异步API,不推荐强行用Task.Run包装(原因见必要性分析),但如果一定要实现异步形式,代码如下:
[HttpPost] public async Task<IActionResult> SetCalcAsync([FromBody] CalcRequest calcRequest) { try { IClacSdk clacSdk = new CalcSdk(); // 将同步计算任务放入线程池执行 ClacResult clacResult = await Task.Run(() => clacSdk.Calc(calcRequest.id, calcRequest.username)); CustomeResponse customeResponse = new CustomeResponse() { ErrorDescription = clacResult.ErrorDescription, ErrorNum = clacResult.ErrorNum, ClacResult = clacResult.Result }; return Ok(customeResponse); } catch (Exception exc) { return StatusCode(500, GetErrorFormat(exc)); } }
二、这么做是否有必要?
核心判断标准是外部DLL的Calc方法属于CPU密集型还是IO密集型:
如果是IO密集型(方法内部包含数据库查询、网络请求、文件读写等需要等待的操作):
非常有必要改成异步。异步能释放当前ASP.NET线程池线程,让它去处理其他请求,大幅提升API的并发吞吐量,避免线程被长时间占用导致请求排队。如果是纯CPU密集型(仅做内存内计算,无任何等待操作):
完全没必要改造。Task.Run会把计算任务转移到线程池的另一个线程,反而增加线程切换开销;而且ASP.NET线程池线程总数有限,CPU密集型任务用异步不会提升性能,甚至可能降低整体吞吐量。
注意:如果是纯CPU密集型任务,强行用Task.Run包装属于“假异步”,对Web服务有害无利,不如保持原同步实现。
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

