带有一个固有异步操作的永久循环的线程

时间:2018-04-12 01:29:19

标签: multithreading asynchronous windows-services service-broker

我试图在Windows服务中启动的无限循环工作线程中理解async / await的语义。我是新手,所以给我一些余地,我试图理解这个概念。

工作线程将永远循环(直到服务停止)并且它处理外部队列资源(在这种情况下是SQL Server Service Broker队列)。

工作线程使用可在服务运行时更改的配置数据,方法是通过某种IPC在主服务线程上接收命令。理想情况下,工作线程应该在等待接收外部队列消息时处理这些配置更改。从服务代理读取本质上是异步的,您实际上会发出一个带有接收超时的“waitfor receive”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循环处理配置更改,代理处理功能需要“轮流”运行。具体来说,当潜在长时间运行的代理接收命令正在运行时,配置更改处理循环将不会运行。

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

据我所知,这不会发生什么,虽然我只是“知道”因为我已经阅读了这些内容。我真的不明白。

可以说现有的控制流(即没有异步调用)是正常的......如果配置更改影响ProcessBrokerMessages()函数(他们可以),那么无论如何在函数运行时都无法更改配置。但这似乎是这个特定例子的特定点。我可以想象一下,配置更改正在改变线程所做的其他事情,与ProcessBrokerMessages()调用无关。

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

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

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

2 个答案:

答案 0 :(得分:1)

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

  • 将取消令牌传递给执行'线程。
  • 设置监视器(响应IPC的设置)还具有对令牌的引用
  • 发生配置更改时,监控会更改配置,然后发出令牌信号
  • 执行线程中止任何挂起的WAITFOR(或实际上消息处理循环中的任何挂起处理,你应该使用取消令牌无处不在)。任何交易都被中止并回滚
  • 使用新的取消令牌重新启动执行线程。它将使用新配置

答案 1 :(得分:0)

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

我的实现是使用锁定,但是从Task.Run()中包含的服务主线程写入配置。读者不必担心任务,因为读者已经在自己的线程中。