使用async/await编写控制台程序调用异步方法时程序挂起求解
嘿,我来帮你排查这个问题!从你给出的代码片段来看,主要有两个可能的原因导致程序挂起,咱们一个个说:
1. 未启动CalculateValue里创建的Task
你在CalculateValue方法中用new Task<int>(GetValue)创建了一个任务,但如果没有调用t.Start()来启动它,这个任务会一直处于「未执行」的状态,永远不会返回结果。这样一来,TestAsync里的await会一直等着这个任务完成,而Main线程调用x.Result时会被死死阻塞,程序自然就挂起了。
举个例子,如果你的CalculateValue是这样写的:
private static Task<int> CalculateValue() { Console.WriteLine("In CalculateValue"); Task<int> t = new Task<int>(GetValue); // 这里漏掉了t.Start()! return t; }
那这个任务根本不会跑起来,结果自然永远等不到。
2. 同步上下文死锁(针对带同步上下文的环境)
如果TestAsync是标准的async方法(比如里面用了await),在WPF、WinForms这类有同步上下文的环境中,await会捕获当前的同步上下文,等任务完成后要回到这个上下文继续执行。但你在Main里调用x.Result会直接阻塞当前线程(也就是持有同步上下文的线程),导致任务没法回到上下文继续执行,形成死锁。不过控制台应用本身没有同步上下文,这个问题在你当前场景下概率较低,但如果后续迁移到其他环境要注意。
怎么修复?
针对Task未启动的问题(最可能的原因)
推荐直接用Task.Run代替new Task,它会自动帮你启动任务,代码更简洁也不容易出错:
private static Task<int> CalculateValue() { Console.WriteLine("In CalculateValue"); return Task.Run(() => GetValue()); }
如果你非要用new Task的写法,记得一定要手动启动任务:
private static Task<int> CalculateValue() { Console.WriteLine("In CalculateValue"); Task<int> t = new Task<int>(GetValue); t.Start(); // 加上这行启动任务 return t; }
针对死锁问题(如果是在有同步上下文的环境)
尽量不要在同步方法里调用Result或Wait()来阻塞线程,最好让Main方法也改成async(.NET Core 3.0及以上支持async Main):
static async Task Main(string[] args) { var x = await TestAsync(); Console.WriteLine(x); }
这样就能避免阻塞,让异步流程自然执行。
另外,你代码里的var task = Task.Factory.StartNew(() => 5);看起来没被用到,可以考虑删掉哦~
内容的提问来源于stack exchange,提问作者Sandeep

