【发布时间】:2022-01-25 12:18:03
【问题描述】:
我有一个用 C# 实现的 Windows 服务,它需要经常做一些工作。我已经使用System.Threading.Timer 和一个负责调度下一个回调的回调方法实现了这一点。我无法优雅地停止(即处置)计时器。这是一些可以在控制台应用程序中运行的简化代码,可以说明我的问题:
const int tickInterval = 1000; // one second
timer = new Timer( state => {
// simulate some work that takes ten seconds
Thread.Sleep( tickInterval * 10 );
// when the work is done, schedule the next callback in one second
timer.Change( tickInterval, Timeout.Infinite );
},
null,
tickInterval, // first callback in one second
Timeout.Infinite );
// simulate the Windows Service happily running for a while before the user tells it to stop
Thread.Sleep( tickInterval * 3 );
// try to gracefully dispose the timer while a callback is in progress
var waitHandle = new ManualResetEvent( false );
timer.Dispose( waitHandle );
waitHandle.WaitOne();
问题是当waitHandle.WaitOne 阻塞时,我在回调线程上从timer.Change 得到一个ObjectDisposedException。我做错了什么?
The documentation 表示我正在使用的 Dispose 重载:
在所有当前排队的回调完成之前,不会释放计时器。
编辑:看来文档中的此声明可能不正确。有人可以验证吗?
我知道我可以通过在回调和处置代码之间添加一些信号来解决这个问题,正如 Henk Holterman 下面建议的那样,但除非绝对必要,否则我不想这样做。
【问题讨论】:
-
为什么不能让定时器每 10 秒自动运行一次呢?为什么要手动重新安排?
-
@Tudor:因为有时工作需要更长的时间,而且我不希望多个回调在它们的执行中重叠。
-
服务停止时为什么要手动暴露定时器?当 AppDomain 卸载时,所有内存都会被回收。
-
@DanielHilgarth:在我的实际实现中,我正在处理来自
ServiceBase.OnStop的计时器。在我让服务关闭之前,我想确保计时器已被释放并且没有正在进行回调。我还有其他需要运行的关闭代码,在确定计时器完全消失之前我不想这样做。 -
我开始怀疑你是否遇到了一个与线程回调引用相关的微妙问题,作为构造函数的匿名委托......如果你(为了测试)将它转换为命名函数...鉴于计时器在辅助 CLR 线程上触发..
标签: c# .net multithreading