【问题标题】:Gracefully stop a Timer callback worker thread优雅地停止 Timer 回调工作线程
【发布时间】:2011-04-25 16:57:15
【问题描述】:

我在我的 Windows 服务中使用System.Threading.Timer 并使用Monitor.TryEnter 锁定回调方法,因此它是不可重入的。在回调内部,我正在循环一些数据库对象(Linq to SQL 实体)并执行一些 IO 任务。在循环的每次迭代中,我都会更改实体的一些属性以将其标记为已处理。循环退出后,我在 datacontext 上调用 SubmitChanges,它将更改持久化到数据库中。出现如下问题:如果在执行回调的时候服务停止了,可能有一些IO任务已经执行了,但是数据库中的记录还没有被标记为已处理(即SubmitChanges还没有被调用)--显然,不是我想要发生的事情。不知何故,我需要与 OnStop 事件已触发的回调工作线程进行通信,以允许它提交更改并完成处理。如何最好地做到这一点?

【问题讨论】:

  • 我认为您担心的是,通过终止计时器,您将终止计时器回调函数的执行,对吗?
  • 不完全。我应该更清楚;我意识到停止计时器只会阻止它再次触发,但不会终止回调方法。但是,一些 IO 任务可能需要长时间运行——可能需要几分钟。如果花费太长时间,我不希望用户(或系统)强行中止该过程。我宁愿它自己优雅地完成它的服务。
  • 嗯,服务控制有 RequestMoreTime() 方法或类似的方法(我是从记忆中说的),您可以使用它来延长服务停止时间,但似乎仍然不会被 SCM 挂起。跨度>

标签: .net windows-services timer threadpool


【解决方案1】:

首先决定是完成回调执行的任务还是回滚它们。因此,如果您决定完成任务,您将执行回调到最后。 OnStop 中的时间应该已经取消了。如果您使用第二个选项(回滚),您的代码将如下所示:

bool shouldAbort=false;

TimerProc()
{
     Step1();
     if (shouldAbort)
     {
         UndoStep1();
         return;
     }
     Step2();
     if (shouldAbort)
     {
         UndoStep2();
         UndoStep1();  //  or vice versa, depending on your operations
         return;
     }
     // ...
}

在 OnStop() 中

timer.Stop();  //  don't worry here - your TimerProc() WILL finish
shouldAbort=true;

【讨论】:

  • 如果您担心 TimerProc() 未完成,请在终止计时器后对其使用 AutoResetEvent 和 WaitOne(),并在 TimerProc() 结束时使用 Set() 事件
  • 感谢您的建议。我 am 在 OnStop 中停止计时器: timer.Change(Timeout.Infinite, 0) -- 正在使用 Threading 命名空间中的 Timer。但我也想向回调线程发出信号以快速结束(不回滚)。也许使需求更加明显的场景是关闭事件。
  • 有两个安东尼:一个希望任务快速结束,另一个不希望长时间的 i/o 任务被强行杀死。应该放弃,让我们给他们一些时间来决定:)
  • 呵呵……也许吧。它实际上是几个不同 IO 进程的序列:创建文件、FTP 文件、发送一些电子邮件、执行一些数据库任务等。每个任务在数据库中都有一个与之关联的完成标志。两个安东尼都想中断序列,而不是单独的 IO 操作。希望这可以解决问题。
  • 那么,嗯,问题出在哪里。按照建议举起一些旗帜,退出,就这样?嗯,我在这里错过了什么吗?
【解决方案2】:

你可以看看使用Task Parallel Library。阅读Task Cancellation 页面。如果我正确理解您的需求,这将为您提供一种创建工作线程的方法,该线程可以干净地整理以响应取消。

【讨论】:

  • 看起来很有趣,可惜我还在用Framework 3.5 :(
猜你喜欢
  • 2019-11-10
  • 2011-03-12
  • 2020-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-05
相关资源
最近更新 更多