【问题标题】:How do I wait for a worker process to finish but also limit it's time to do so?我如何等待工作进程完成但又限制它的时间?
【发布时间】: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&lt;T&gt; 委托并调用其Report() 方法。如果需要,可以在Task&lt;T&gt;T 对象中返回完成结果。
  • 我以前从未使用过任务,但我会看看我能弄清楚什么。
  • 这里有一些不同的例子:Start a Task in the Form Shown event。了解您当前在 DoWork 处理程序中执行的操作类型、是否调用 ReportProgress() 以及使用什么数据以及 BGW 终止时会发生什么(即,如果您正在执行RunWorkerCompleted 中的其他操作)。一般来说,如果您正在执行 IO 密集型工作或 CPU 密集型工作。
  • 当前 BGW 正在访问 URL,获取数据,并将其插入 SQL 数据库。它遍历大量的 URL,有时多达 2k,但检索到的数据很小(20 个字符)。它目前不支持ReportProgressRunWorkerCompleted 中唯一的东西是一个用 date.now 更新的 lastrun 变量,所以我可以跟踪它需要运行多长时间。
  • 所以,IO-bound,它也支持 async/await 模式,因为你有所有可等待的方法。不清楚你在用你下载的数据做什么。只需更新一个数据库,全部在 DoWork 处理程序中?数据库更新也(通常)是可等待的(例如,SqlCommand.ExecuteReaderAsync()SqlCommand.ExecuteNonQueryAsync(() 等)并且具有接受 CancellationToken 的重载(因此,隐式地,也是超时,因为这是由创建 CancellationToken 的 CancellationTokenSource 设置的)。

标签: windows vb.net service


【解决方案1】:

你已经接近了,如果你不想休眠 (1000) 并锁定东西,请执行休眠 (1)。

    'Dim x As Integer = 0
    'While ((DataRetrievalBackgroundWorker.IsBusy) Or (x < 15))
    '    Threading.Thread.Sleep(1000)
    '    x += 1
    'End While

    Dim T As Date = Now.AddSeconds(15)
    While DataRetrievalBackgroundWorker.IsBusy Or Now() < T
        Threading.Thread.Sleep(1)
        Application.DoEvents()
    End While

【讨论】:

  • 是的,我想我也可以这样做。真正的问题是,如果我使用后台工作人员进行实际工作是否重要…… sleep(1000) “不应该”阻止它完成,因为它在自己的线程上,对吧?其他选项是查看@Jimi 发布的任务内容,我仍在尝试弄清楚。
  • 我忘了添加 DoEvents...是的,后台工作人员无论如何都不会被阻塞。
  • 我不喜欢任务,一旦你去任务,你就不能回去了。我可以将一个 sub 变成一个线程,并随心所欲地调用它。你不能用任务来做到这一点。一旦一个子是一个任务,它就是一个唯一的任务。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-16
  • 2012-03-16
  • 2010-11-06
相关资源
最近更新 更多