何时设置允许无效异步操作,在调用异步操作结果时

本文关键字:异步操作 调用 结果 无效 设置 许无效 何时 | 更新日期: 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。有两件事需要检查:

  1. 确保在 .NET 4.5 上运行。我怀疑你已经是了,否则你根本看不到那条消息。
  2. 确保将UseTaskFriendlySynchronizationContext设置为 true httpRuntime.targetFramework设置为 4.5

更新:经过思考,在 MVC 中还有另外两个条件可能导致这种情况 ASP.NET:

  1. 确保未调用任何async void方法。
  2. 确保您未使用 EAP 组件。例如:
    1. HttpClient是 TAP,所以它会起作用。
    2. HttpWebRequest是APM,所以围绕它的TAP包装器可以工作。
    3. WebClient是 EAP,因此会导致此错误。无论您使用DownloadStringAsync还是DownloadStringTaskAsync,都是如此。
找不到将

UseTaskFriendlySynchronizationContext 设置为 true 的代码,因为设置它的代码不是 .NET 框架的一部分。

操作同步上下文的代码由 c# 5.0 编译器在编译期间生成。C# 编译器创建一个状态机结构,其名称很奇怪,如 <yourmethod>d__0(以避免与代码冲突(。在这个结构中有一个叫做"MoveNext"的方法。这是捕获同步上下文的位置。你的大部分异步代码也会被编译器移动到那里。