更新
经过一番思考,我得出结论,我可能没有问对正确的问题。
给定以下代码:
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);
}
}
很明显(尤其是在阅读了@Stephen Cleary 的博文之后),ActionResult Index()
代码会导致死锁。但为什么?
经过一番挖掘,我发现它.NET 4.5
引入了一个新AspNetSynchronizationContext
的,应该更“任务友好”。下载源代码.NET 4.5
并查看新的内部AspNetSynchronizationContext
,我认为调用OperationStarted
将导致对名为AllowVoidAsyncOperations
. 如果该值为 true,则没有问题。但是,如果此布尔值的值为 false,它将引发以下异常:
此时无法启动异步操作。异步操作只能在异步处理程序或模块内或在页面生命周期中的某些事件期间启动。如果在执行页面时发生此异常,请确保将页面标记为 <%@ Page Async=\"true\" %>。
经过大量反思,我认为,调用 theasync Task<ActionResult> IndexWorks()
以某种方式将 the 设置AllowVoidAsyncOperations
为 true。- 调用同步版本时,它保持默认值:false。
因此,我的问题是:
什么时候异步 ActionResult 调用内部方法AspNetSynchrnoizationContext
,设置AllowVoidAsyncOperations
为 true?- 到目前为止,我已将其范围缩小到班级CallHandlerExecutionStep
内的HttpApplication
班级。- 但是,我不确定它如何决定是否允许它。