【问题标题】:Background worker thread and async calls后台工作线程和异步调用
【发布时间】:2020-05-07 20:15:32
【问题描述】:

我正在开发一个 c# 应用程序,它有一个后台线程来处理很多任务,例如调用 HTTP/REST 端点,进行基本计算,通过套接字发送数据等。所有操作都排队到后台线程,它单独将它们出列,处理它们,然后到下一个。

我一直在考虑在后台线程中使用异步,例如使用 async/await 语义调用 HTTP 端点,但不确定这将如何影响后台线程。让我们说后台线程使用异步调用 HTTP 端点,我如何构造代码以便它出列下一个数据包并处理它?

【问题讨论】:

  • 你看过这个:stackoverflow.com/questions/20261300/…。它解决了以正确方式结合这两种技术的问题。
  • 停止使用后台工作人员 - 他们现在已经过时了。改为尝试任务 - 它们专门与 async/await 一起使用。
  • 是否允许多个异步操作同时运行?或者您希望每个操作都在前一个操作完成后开始?

标签: c# multithreading async-await


【解决方案1】:

我一直在考虑在后台线程中使用异步,例如使用 async/await 语义调用 HTTP 端点,但不确定这将如何影响后台线程。让我们说后台线程使用异步调用 HTTP 端点,我如何构造代码以便它出列下一个数据包并处理它?

await 具有“挂钩”,可用于控制默认恢复行为。 await 本身(如果一直使用)将“屈服”回您的消息处理循环;那没问题。但默认情况下,当await 恢复执行其方法时,该方法将在线程池线程上运行,而不是在您的专用后台线程上。

如果您希望await 之后的代码在您的后台线程上恢复,您需要创建一个SynchronizationContext 将工作排入后台线程的队列,并确保将其设置为当前的SynchronizationContext在后台线程上运行的任何代码。我写了一个AsyncContextThread,它是一个带有消息队列的后台线程和SynchronizationContext;这应该是一个很好的起点。

【讨论】:

  • 他为什么要继续在同一个线程上继续?有什么特别的原因吗?
  • @Piotr:这通常是预期的行为。如果开发人员将异步方法排队到后台线程运行器,他们通常会惊讶地发现该方法在其await 之后跳转到线程池线程。
  • 没错,所以我想没有明显的理由在同一个(后台)线程上恢复。
【解决方案2】:

您可以混合使用 async/await 和在后台线程上运行。 Async/Await 无论如何都不会“影响”后台线程。但请记住,在 async/await 下面有涉及的任务(和状态机)。

有趣的事实是 - 当你反编译 async/await 代码时 --> 那里没有 async/await;)你可以说这是一个语法糖。

有关其组织方式的更多信息 - 例如此处:https://ranjeet.dev/understanding-how-async-state-machine-works/

因此,当您从异步操作返回时(例如,您将开始接收来自 HTTP 请求的响应),可能会发生这种情况 - 您最终可能会进入不同的线程,其余代码将在一个不同的线程。通常这不是问题,但有时它确实会产生影响(例如在 ASP.NET 上 - httpcontext 可能会因此而丢失)

您确实还问过“我如何构建代码”这就是异步/等待的美妙之处。你不需要 :) 你所要做的就是把所有的东西都改成 async/await - 因为规则:“Async all way”非常重要” 更多关于异步编程的重要规则在这里: https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming

例如,如果您确实有这样的代码:

public string SendHttpRequest()
{
    using (var client = new WebClient())
    {
        return client.DownloadString(new Uri("http://www.example.com/example.aspx"));
    }
}

您只需将其更改为:

public async Task<string> SendHttpRequestAsync()
{
    using (var client = new WebClient())
    {
        return await client.DownloadStringTaskAsync("http://www.example.com/example.aspx");
    }
}

然后当然是代码中的每个地方 - 你必须更改为异步(并且你必须调用此方法以及所有将与等待异步的方法)这就是“一路异步”的规则不要'不要试图在代码中的某处这样使用它:

SendHttpRequestAsync().Result --> beacause it saves you from adding async on the method;)

然后你错过了使用异步的重点,并且真的会发生一些事情(尝试在 Winforms 中通过一些 OnClick 事件来做这样的事情:))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多