如何在C#中正确结合async/await使用AcquireTokenForClient?
正确使用async/await调用AcquireTokenForClient的方案
你用task.Result确实存在死锁风险,尤其是在UI线程或者传统ASP.NET这类存在SynchronizationContext的环境里。原因是await默认会捕获当前上下文,当异步方法执行完要回到这个上下文时,主线程已经被Result阻塞住,两边互相等待就形成了死锁。
最优解决方案:异步到底
把fun改成异步方法,全程用await替代Result,这是async/await设计的核心用法,能彻底规避死锁:
async Task FunAsync() { _service = await InitServiceAsync(); }
如果调用FunAsync的地方也是异步方法,就继续用await;如果是控制台程序的Main方法这类入口点,可按以下方式处理:
// .NET Core及以上支持异步Main static async Task Main(string[] args) { await FunAsync(); // 后续逻辑 } // 旧框架不支持异步Main时,用GetAwaiter().GetResult()(控制台环境安全,无SynchronizationContext) static void Main(string[] args) { FunAsync().GetAwaiter().GetResult(); }
特殊场景:无法修改为异步方法的情况
如果因限制必须保持fun为同步方法,可以把异步操作放到线程池线程执行,避免上下文冲突:
void Fun() { _service = Task.Run(() => InitServiceAsync()).Result; }
线程池线程没有绑定的SynchronizationContext,不会出现死锁,但这种方式会额外占用线程池资源,仅适合非UI场景临时使用。
补充说明
你的InitServiceAsync里已经用了ConfigureAwait(false),这是个好习惯——它告诉await不需要回到原上下文,能进一步降低死锁概率,但这不能抵消task.Result本身的阻塞风险,所以优先还是采用异步到底的模式。
内容的提问来源于stack exchange,提问作者Yuan
相关产品推荐
相关产品推荐

