【问题标题】:thread with a forever loop with one inherently asynch operation具有永久循环的线程,具有一个固有的异步操作
【发布时间】:2018-04-12 01:29:18
【问题描述】:

我试图了解在 Windows 服务内启动的无限循环工作线程中异步/等待的语义。我是这方面的新手,所以在这里给我一些余地,我正在努力理解这个概念。

工作线程将永远循环(直到服务停止)并处理外部队列资源(在本例中为 SQL Server Service Broker 队列)。

工作线程使用的配置数据可以在服务运行时通过某种 IPC 在主服务线程上接收命令来更改。理想情况下,工作线程应在等待接收外部队列消息时处理这些配置更改。从服务代理读取本质上是异步的,您实际上是发出带有接收超时的“等待接收”TSQL 语句。

但我不太了解为此需要使用的控制流程。

假设我使用 concurrentQueue 将配置更改消息从主线程传递到工作线程。然后,如果我做了类似...

void ProcessBrokerMessages() {
   foreach (BrokerMessage m in ReadBrokerQueue()) {
      ProcessMessage(m);
   }
}

// ... inside the worker thread:
while (!serviceStopped) {
   foreach (configChange in configChangeConcurrentQueue) {
      processConfigChange(configChange);
   }
   ProcessBrokerMessages();
}

...然后处理配置更改的 foreach 循环和代理处理功能需要“轮流”运行。具体来说,config-change-processing 循环不会在可能长时间运行的代理接收命令运行时运行。

我的理解是,在这种情况下,简单地将 ProcessBrokerMessages() 转换为异步方法对我没有帮助(或者我不明白会发生什么)。对我来说,由于我缺乏理解,最直观的解释似乎是,当我点击异步调用时,它会关闭并做它的事情,并且执行将继续外部 while 循环的重新启动......但这会意味着循环也会一遍又一遍地执行 ProcessBrokerMessages() 函数,即使它已经从前一个循环中的调用运行,这是我不想要的。

据我所知,这不会发生,尽管我只是“知道”这一点,因为我已经读过一些类似的东西。不是很懂。

可以说,现有的控制流程(即,没有异步调用)是可以的……如果配置更改影响 ProcessBrokerMessages() 函数(它们可以),那么在函数运行时配置不能被更改。但这似乎是这个特定示例的特定点。我可以想象配置更改正在更改线程所做的其他事情的情况,与 ProcessBrokerMessages() 调用无关。

有人可以在这里提高我的理解吗?什么才是正确的拥有方式

  • 循环多个语句的代码块
  • 其中一个(或一些)但并非所有这些语句都是异步的
  • 并且异步操作一次只能执行一次
  • 但在异步操作的单个实例运行时,执行应继续循环遍历其余语句
  • 如果之前的调用已完成,则应在循环中再次调用异步方法

似乎我可以使用 BackgroundWorker 来运行接收语句,该语句在其工作完成时会翻转一个标志,但在我看来也很奇怪创建一个专门用于处理外部资源的线程,然后在该线程内,创建一个 BackgroundWorker 来实际完成这项工作。

【问题讨论】:

    标签: multithreading asynchronous windows-services service-broker


    【解决方案1】:

    您可以使用CancelationToken。大多数异步函数都接受一个作为参数,如果令牌发出信号,它们会取消调用(实际上是返回的任务)。 SqlCommand.ExecuteReaderAsync(您很可能会使用它来发出WAITFOR RECEIVE)。所以:

    • 将取消令牌传递给“执行”线程。
    • 设置监视器(响应 IPC 的监视器)也有对令牌的引用
    • 发生配置更改时,监控会进行配置更改,然后发出令牌信号
    • 执行线程中止任何挂起的 WAITFOR(或实际上在消息处理循环中的任何挂起处理,您应该在任何地方使用取消标记)。任何事务都被中止并回滚
    • 使用新的取消令牌重新启动执行线程。它将使用新的配置

    【讨论】:

    • 感谢 Remus,当涉及到服务代理时,我们总是可以依靠您! :) 这本身就是一个好主意,但不是我想要的。我不想取消接收,我希望接收能够在 IPC 调用进入并被“处理”时保持异步运行。接收语句可以在下一次运行时获取与自身相关的任何更改(例如批量大小或超时)。我觉得我应该在这里完全考虑不同的控制流。
    【解决方案2】:

    因此,在这种特殊情况下,我决定采用更简单的共享状态解决方案。这在原则上当然是一个不太健全的解决方案,但是由于没有涉及很多共享状态,而且由于整个应用程序不是很复杂,所以它似乎可以原谅。

    我在这里的实现是使用锁定,但是将服务主线程中的配置写入包裹在 Task.Run() 中。由于阅读器已经在自己的线程中,因此阅读器不会打扰任务。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-11
      • 1970-01-01
      • 1970-01-01
      • 2016-04-07
      相关资源
      最近更新 更多