【发布时间】:2021-03-24 14:45:00
【问题描述】:
我正在 VB.Net 中编写一个 Windows 服务,它将发送到一些设备和数据记录点的信息。我正在使用后台工作人员来执行此操作,因此服务本身仍然是响应式的。我有一个每秒运行的计时器并检查当前时间的分钟部分。每次分钟组件更改时,我都会检查哪些设备需要检查,有些是每分钟一次,有些是每 5 次,有些是每 10 次,等等。这些过程可能需要几秒钟或一分钟以上(如果不是,我只会重新运行工作程序如果最后一个进程花费的时间比数据检索间隔长,则已运行并记录错误)。
在我的 OnStop 服务事件中,我想确保所有工作人员都关闭。我在工作人员上调用 CancelAsync 并且工作人员检查取消以希望干净地退出(即检查取消,如果错误检索数据,将数据保存到数据库中,循环)。
我的问题是我不想使用 sleep 语句,因为它会锁定所有内容,但我也不希望服务永远不会关闭。例如,我目前有这个:
Protected Overrides Sub OnStop()
' Add code here to perform any tear-down necessary to stop your service.
My.Application.Log.WriteEntry("ServiceABC shutting down for device " & DeviceID)
ServiceTimer.enable = false
If DataRetrievalBackgroundWorker.IsBusy Then
DataRetrievalBackgroundWorker.CancelAsync()
Dim x As Integer = 0
While ((DataRetrievalBackgroundWorker.IsBusy) Or (x < 15))
Threading.Thread.Sleep(1000)
x += 1
End While
End If
End Sub
这应该可以工作,因为后台工作人员在另一个线程上,对吗?有没有更好的方法来处理这个问题?
【问题讨论】:
-
使用 Task 而不是 BackGroundWorker,等待它,传递一个带有超时设置的 CancellationToken 及其 CancellationTokenSource。
ReportProgress过程被替换为传递几乎相同的Progress<T>委托并调用其Report()方法。如果需要,可以在Task<T>的T对象中返回完成结果。 -
我以前从未使用过任务,但我会看看我能弄清楚什么。
-
这里有一些不同的例子:Start a Task in the Form Shown event。了解您当前在
DoWork处理程序中执行的操作类型、是否调用ReportProgress()以及使用什么数据以及 BGW 终止时会发生什么(即,如果您正在执行RunWorkerCompleted中的其他操作)。一般来说,如果您正在执行 IO 密集型工作或 CPU 密集型工作。 -
当前 BGW 正在访问 URL,获取数据,并将其插入 SQL 数据库。它遍历大量的 URL,有时多达 2k,但检索到的数据很小(20 个字符)。它目前不支持
ReportProgress,RunWorkerCompleted中唯一的东西是一个用 date.now 更新的 lastrun 变量,所以我可以跟踪它需要运行多长时间。 -
所以,IO-bound,它也支持 async/await 模式,因为你有所有可等待的方法。不清楚你在用你下载的数据做什么。只需更新一个数据库,全部在
DoWork处理程序中?数据库更新也(通常)是可等待的(例如,SqlCommand.ExecuteReaderAsync()、SqlCommand.ExecuteNonQueryAsync(()等)并且具有接受 CancellationToken 的重载(因此,隐式地,也是超时,因为这是由创建 CancellationToken 的 CancellationTokenSource 设置的)。