在 MVC 中从同步调用异步而没有等待时,TPL 任务死锁

2024-04-17

我知道在同步 MVC 方法中调用异步方法,同时使用 .Wait() 或 .Result 等待任务完成时,存在 TPL 死锁陷阱。

但我们刚刚在 MVC 应用程序中发现了一个奇怪的行为:同步操作调用异步方法,但由于它是触发器,因此我们从未等待它完成。尽管如此,异步方法似乎还是卡住了。

代码如下,这个奇怪的问题并不是100%发生的。它只是有时会发生。

当它发生时:

  1. HomeController.Index() 操作已完成
  2. Log.Info("Begin") 已执行。
  3. SaveToDb() 完成了这项工作,但未知完成后是否挂起。
  4. PublishTomessageQueue() 无法完成这项工作,不知道它是否从未启动或只是卡在里面。
  5. Log.Info("Finish")/Log.Error("Error") 都没有被调用。

大多数时候,代码会按预期工作。

ISomeInterface.Trigger() 也被从其他地方调用,Windows 服务而不是 mvc,但这种奇怪的行为永远不会发生。

所以我的问题是,异步任务是否有可能陷入死锁没有 .Wait() 也没有 .Result?

非常感谢。

public interface ISomeInterface
{
    Task Trigger();
}

public class SomeClass
{
    public async Task Trigger()
    {
        Log.Info("Begin");

        try
        {
            await SaveToDb();

            await PublishToMessageQueue();

            Log.Info("Finish");
        }
        catch (Exception ex)
        {
            Log.Error("Error");
        }
    }
}

public class HomeController : Controller
{
    public ISomeInterface Some { get; set; }

    public ActionResult Index()
    {

        Some.Trigger(); //<----- The thread is not blocked here.

        return View();
    }


}

异步方法似乎卡住了......有时会发生......大多数时候,代码按预期工作。

是的。这段代码有几个主要问题。

首先,它可以尝试恢复不再存在的请求上下文。例如,请求Index进来,ASP.NET 为该线程创建一个新的请求上下文。然后它调用Index在该请求上下文中,并且Index calls Some.Trigger, 什么时候Trigger击中第一个await, it 默认情况下捕获该上下文 https://blog.stephencleary.com/2012/02/async-and-await.html并返回一个未完成的任务Index. Index然后返回,通知 ASP.NET 请求已完成; ASP.NET 发送响应,然后删除该请求上下文。稍后的,Trigger准备好在其后恢复await,并尝试恢复该请求上下文...但它不再存在(请求已完成)。混乱随之而来。

第二个主要问题是,这是“即发即忘”,这是一个真是个坏主意在 ASP.NET 上 https://blog.stephencleary.com/2014/06/fire-and-forget-on-asp-net.html。这是一个坏主意,因为 ASP.NET 完全是围绕请求/响应系统设计的;它对于处理请求中不存在的代码的功能非常有限。当没有活动请求时,ASP.NET 可以(并且将会)定期回收您的应用程序域和工作进程(这是required以保持物品清洁)。它完全不知道你的Trigger代码正在运行,因为调用它的请求已经完成 - 因此,您正在运行的代码可能会定期消失。

最简单的解决方案是将此“触发”代码移至实际请求中。例如。,Index can await返回的任务Trigger。或者让您的页面代码向 API 发出 AJAX 调用,该 API 调用Trigger (and awaits it).

如果这是不可行的,那么我会推荐一个合适的分布式系统:Index将“触发请求”放入可靠队列中并由独立后端(例如 Win32 服务)处理。或者您可以使用现成的解决方案,例如Hangfire https://www.hangfire.io/.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 MVC 中从同步调用异步而没有等待时,TPL 任务死锁 的相关文章

  • 当其源是 https uri 时如何使 wpf MediaElement 播放

    在 wpf 独立应用程序 exe 中 我在主窗口中包含了 MediaElement
  • libtool 在 Ubuntu 13.04 上构建 thrift 0.9.1 时出错

    在 Ubuntu 13 04 上构建 thrift 0 9 1 支持 C C java C perl python 时出现此错误 configure 不带任何选项运行 make 不带任何选项运行 Making all in test mak
  • 如何查明 .exe 是否正在 C++ 中运行?

    给定进程名称 例如 程序 exe C 标准库没有这样的支持 您需要一个操作系统 API 来执行此操作 如果这是 Windows 那么您将使用 CreateToolhelp32Snapshot 然后使用 Process32First 和 Pr
  • 以下 PLINQ 代码没有改进

    我没有看到使用以下代码的处理速度有任何改进 IEnumerable
  • C# Winforms Designer 无法打开,因为它无法在同一程序集中找到类型

    我收到以下错误 找不到类型 My Special UserControl 请确保引用包含此类型的程序集 如果此类型是您的开发项目的一部分 请确保已使用当前平台或任何 CPU 的设置成功构建该项目 但没有任何意义的是My Special Us
  • 关闭整数的最右边设置位

    我只需要关闭最右边的设置位即可 我的方法是找到最右边位的位置 然后离开该位 我编写这段代码是为了这样做 int POS int n int p 0 while n if n 2 0 p else break n n 2 return p i
  • C 类型命名约定,_t 或 ALLCAPS

    我一直想知道是否有任何命名约定 例如何时对类型使用全部大写以及何时追加 t 什么时候不使用任何东西 我知道当时 K R 发布了各种有关如何使用 C 的文档 但我找不到任何相关内容 在 C 标准库类型中 t看起来漂亮占主导地位 time t
  • 为什么 std::function 不是有效的模板参数,而函数指针却是?

    我已经定义了名为的类模板CallBackAtInit其唯一目的是在初始化时调用函数 构造函数 该函数在模板参数中指定 问题是模板不接受std function作为参数 但它们接受函数指针 为什么 这是我的代码 include
  • 如何设置消息队列的所有者?

    System Messaging MessageQueue 类不提供设置队列所有权的方法 如何以编程方式设置 MSMQ 消息队列的所有者 简短的答案是 p invoke 对 windows api 函数的调用MQSetQueueSecuri
  • 从点云检测平面集

    我有一组点云 我想测试3D房间中是否有角落 所以我想讨论一下我的方法 以及在速度方面是否有更好的方法 因为我想在手机上测试它 我将尝试使用霍夫变换来检测线 然后我将尝试查看是否有三条线相交 并且它们也形成了两个相交的平面 如果点云数据来自深
  • 在 C 语言中替换宏内的宏

    我正在尝试使代码部分可重用 我下面的评论片段没有达到我想要的效果 define NAME ABC define LOG SIZE NAME LEN 我想LOG SIZE决心ABC LEN 我尝试过使用 但没能让它发挥作用 LOG SIZE在
  • 在 C++ 代码 gdb 中回溯指针

    我在运行 C 应用程序时遇到段错误 在 gdb 中 它显示我的一个指针位置已损坏 但我在应用程序期间创建了 10 万个这样的对象指针 我怎样才能看到导致崩溃的一个 我可以在 bt 命令中执行任何操作来查看该指针的生命周期吗 谢谢 鲁奇 据我
  • WinForms - 加载表单时如何使用 PaintEventArgs 运行函数?

    我试图理解图形 在 Graphics FromImage 文档中 它有这样的示例 private void FromImageImage PaintEventArgs e Create image Image imageFile Image
  • 在 mvc4 中创建通用 mvc 视图

    我以前也提过类似的问题 没有得到答案 如何创建一个通用的 mvc4 视图 该视图可以显示传递给它的模型列表或单个模型 模型可以是个人 组织或团体 无论传递给它的是什么 如果您正在寻找类似的东西 model MyViewModel
  • WPF DataGrid - 在每行末尾添加按钮

    我想在数据网格的每一行的末尾添加一个按钮 我找到了以下 xaml 但它将按钮添加到开头 有人知道如何在所有数据绑定列之后添加它吗 这会将按钮添加到开头而不是末尾
  • ASP.NET MVC 粘贴到剪贴板

    我有一个 ASP NET MVC 4 应用程序 我想复制文本 从 PDF CTRL C 并将其作为参数粘贴到控制器的方法中 我的网络网格有一个带有 ActionLink 的列 grid Column format a href Url Ac
  • 如何测试某些代码在 C++ 中无法编译? [复制]

    这个问题在这里已经有答案了 可能的重复 单元测试编译时错误 https stackoverflow com questions 605915 unit test compile time error 我想知道是否可以编写一种单元测试来验证给
  • 时间:2019-03-17 标签:c#TimerStopConfusion

    我想通过单击按钮时更改文本颜色来将文本框文本设置为 闪烁 我可以让文本按照我想要的方式闪烁 但我希望它在闪烁几次后停止 我不知道如何在计时器触发几次后让它停止 这是我的代码 public Form1 InitializeComponent
  • 运行 xunit 测试时无法将输出打印到控制台窗口

    public class test2InAnotherProject private readonly ITestOutputHelper output public test2InAnotherProject ITestOutputHel
  • 如何知道 HTTP 请求标头值是否存在

    我确信这很简单 但是却让我感到厌烦 我在 Web 应用程序中使用了一个组件 它在 Web 请求期间通过添加标头 XYZComponent true 来标识自身 我遇到的问题是 如何在视图中检查此组件 以下内容不起作用 if Request

随机推荐