何时设置允许无效异步操作,在调用异步操作结果时
本文关键字:异步操作 调用 结果 无效 设置 许无效 何时 | 更新日期: 2023-09-27 18:17:31
>UPDATE
经过一番思考,我得出的结论是,我可能没有问正确的问题。
给定以下代码:
public class HomeController : Controller
{
public Task<string> DownloadAsync(string url)
{
using (var web = new WebClient())
{
return web.DownloadStringTaskAsync(url);
}
}
// THROWS EXCEPTION
public ActionResult Index()
{
var data = DownloadAsync("http://google.dk");
return Content(data.Result);
}
// WORKS
public async Task<ActionResult> IndexWorks()
{
var data = await DownloadAsync("http://google.dk");
return Content(data);
}
}
很明显(尤其是在阅读了 Cleary 的博客文章@Stephen之后(,ActionResult Index()
代码将导致死锁。但是为什么?
经过一番挖掘,我发现.NET 4.5
引入了一个新的AspNetSynchronizationContext
,它应该更"任务友好"。下载.NET 4.5
的源代码并查看新AspNetSynchronizationContext
内部,我认为调用OperationStarted
将导致对名为AllowVoidAsyncOperations
的布尔值进行检查。如果值为 true,则没问题。但是,如果此布尔值为 false,它将引发以下异常:
此时无法启动异步操作。异步 操作只能在异步处理程序中启动,或者 模块或在页面生命周期中的某些事件期间。如果这个 执行页面时发生异常,请确保页面 标记为 <%@ 页面异步=''"true''" %>。
通过大量的思考,我认为,调用async Task<ActionResult> IndexWorks()
以某种方式将AllowVoidAsyncOperations
设置为真。 - 调用同步版本时,它保持默认值:false。
因此,我的问题是:
异步操作结果何时调用 AspNetSynchrnoizationContext
的内部方法,将AllowVoidAsyncOperations
设置为 true? - 到目前为止,我已经将其缩小到HttpApplication
类中的类CallHandlerExecutionStep
。 - 但是,我不确定它如何决定是否允许它。
如果您尝试在请求生命周期中不允许的点(或完全在请求上下文之外(执行异步操作,ASP.NET 会引发此特定异常。
你不应该在MVC中看到 ASP.NET。有两件事需要检查:
- 确保在 .NET 4.5 上运行。我怀疑你已经是了,否则你根本看不到那条消息。
- 确保将
UseTaskFriendlySynchronizationContext
设置为true
或将httpRuntime.targetFramework
设置为4.5
。
更新:经过思考,在 MVC 中还有另外两个条件可能导致这种情况 ASP.NET:
- 确保未调用任何
async void
方法。 - 确保您未使用 EAP 组件。例如:
-
HttpClient
是 TAP,所以它会起作用。 -
HttpWebRequest
是APM,所以围绕它的TAP包装器可以工作。 -
WebClient
是 EAP,因此会导致此错误。无论您使用DownloadStringAsync
还是DownloadStringTaskAsync
,都是如此。
-
UseTaskFriendlySynchronizationContext 设置为 true 的代码,因为设置它的代码不是 .NET 框架的一部分。
操作同步上下文的代码由 c# 5.0 编译器在编译期间生成。C# 编译器创建一个状态机结构,其名称很奇怪,如 <yourmethod>d__0
(以避免与代码冲突(。在这个结构中有一个叫做"MoveNext"的方法。这是捕获同步上下文的位置。你的大部分异步代码也会被编译器移动到那里。