ASP.NET Core异步延续的多线程相关问题咨询
ASP.NET Core异步Action的线程与共享状态问题
一、await前后的线程是否会不同?
是的,ASP.NET Core中不存在同步上下文,await异步操作完成后,延续代码会被调度到ThreadPool线程上执行。而ThreadPool的线程是动态复用的,await之前的代码可能在某个ThreadPool线程(或请求初始线程)上运行,await之后的延续代码大概率会分配到另一个ThreadPool线程,所以前后线程ID通常是不同的。
不过要注意:单个请求的Action内,await前后的代码是串行执行的,不会出现同一请求内的并行情况,只是执行的线程可能发生切换。
二、共享对象是否需要线程安全处理?
必须要关注,而且必须做线程安全处理,核心原因有两点:
- 多个请求的Action方法会被不同的ThreadPool线程同时执行,当它们访问同一个共享对象时,会出现多线程并发访问的场景;
- 就算是单个请求的Action,若共享对象被其他请求的Action同时访问,同样会触发竞态条件,导致数据不一致或异常。
反例:不安全的共享状态
// 静态变量作为共享状态,多线程并发访问会出现计数错误 public static int VisitCount = 0; public async Task<IActionResult> RecordVisit() { // 模拟异步IO操作,比如查询数据库 await Task.Delay(50); // 多个线程同时执行此行时,会出现竞态条件 VisitCount++; return Ok($"当前访问量:{VisitCount}"); }
线程安全的解决方式
方式1:使用lock互斥锁
通过锁确保同一时间只有一个线程访问共享状态:
private static readonly object _lockObj = new object(); public static int VisitCount = 0; public async Task<IActionResult> RecordVisit() { await Task.Delay(50); lock(_lockObj) { VisitCount++; } return Ok($"当前访问量:{VisitCount}"); }
方式2:使用线程安全类型
利用.NET内置的线程安全类型,这类类型内部已实现线程安全机制,无需手动加锁:
public static int VisitCount = 0; public async Task<IActionResult> RecordVisit() { await Task.Delay(50); // 线程安全的递增操作 Interlocked.Increment(ref VisitCount); return Ok($"当前访问量:{VisitCount}"); }
内容的提问来源于stack exchange,提问作者underthevoid
相关产品推荐
相关产品推荐

