【问题标题】:Async method call from ThreadPool thread deadlocks来自 ThreadPool 线程死锁的异步方法调用
【发布时间】:2022-01-24 03:18:59
【问题描述】:

我知道有类似的问题,但我尝试了那里解释的所有最佳实践,但代码仍然死锁。

System.Timers.TimerElapsed 事件调用异步方法,并将SynchronizingObject 设置为null,因此事件将在ThreadPool 上触发。所以实际上在 ThreadPool 上运行 Task.Run().Result 时,我也会遇到死锁,因为 Task.Run 会在 ThreadPool 上触发线程...

我需要使用来自 Windows 服务的 HttpClient 进行 REST 调用,该服务每隔几秒就会在 ThreadPool 上触发一些 System.Timers.Timer 事件。因为HttpClient只有Async方法,所以我需要从ThreadPool调用异步方法。

我尝试了这里所说的所有最佳实践:https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

目前,使整个应用程序异步兼容并不是一个选项,因为它需要大量重构。我需要让它从同步代码中工作。

所以事实上,我等待HttpClient 方法是这样的:

await HttpClient.PostAsync().ConfigureAwait(false);

而且每次等待也有ConfigureAwait(false);,直到我必须得到任务结果的地方。

我尝试了.Result.GetAwaiter().GetResult(),但这些会导致死锁和代码无限挂起。然后我必须从任务管理器中杀死服务进程...

关于如何在不使服务死锁的情况下从任务中获取结果的任何帮助?

我还没有尝试Task.Run<>(async () => await HttpClient.PostAsync());,这能解决我的问题吗?但它看起来像一个黑客......

并且请不要告诉我使用一些外部Nuget包,应该有一种不依赖外部包的方法。

【问题讨论】:

  • 这没有意义。在异步代码上同步等待时会发生死锁,但只有在存在同步上下文且 Windows 服务没有同步上下文时才会发生。
  • 尽管发生这种情况根本不合理,但您是否尝试过在任何地方都不使用await?例如HttpClient.PostAsync().GetAwaiter().GetResult()
  • 你能发布一个简短但完整的导致死锁的代码吗?从你的问题看不清楚。
  • 理想情况下,您可以弄清楚为什么会出现同步上下文。您的 Windows 服务是否有可能在某处具有手动更改任务调度程序以使用同步上下文的代码?除此之外,下一个最好的事情是“一路异步”。由于您已经说过这将需要太多重构,因此您被困在一个“hacky”选项中,该选项涉及使用单独的线程,例如 Task.Run 方法。使用 Task.Run 您甚至可以在该 lambda 中同步阻塞并获取结果,因此您不必将所有其余代码更改为异步。
  • @Gabriel 使用 .GetAwaiter().GetResult() 几乎不应该被建议 - 这是问题的原因,而不是修复(它基本上类似于 .Result ); await 几乎在所有情况下都应该是首选

标签: c# asynchronous async-await task-parallel-library


【解决方案1】:

使您的 Elapsed 事件异步。在您的评论中,您提到您不想这样做,因为 Elapsed 事件可以在第一个事件完成之前再次触发。为避免这种情况,只需在创建计时器时将AutoReset 设置为false,这样它就不会自动重新启动计时器。然后在 Elapsed 事件结束时重新启动它:

private async void OnTimedEvent(object source, ElapsedEventArgs e) {
    try {
        // Do stuff
    } finally {
        // Restart the timer
        timer.Enabled = true;
    }
}

try/finally 块确保即使出现异常也会重新启动计时器。如果需要,您也可以在其中添加 catch 块。

这样,您无需在 Elapsed 事件中使用 lock

【讨论】:

    猜你喜欢
    • 2023-03-24
    • 2017-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-12
    • 2020-09-12
    • 1970-01-01
    • 2021-07-13
    相关资源
    最近更新 更多