17

我有一个用 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();

问题是我在阻塞时ObjectDisposedExceptiontimer.Change回调线程上得到一个。waitHandle.WaitOne我究竟做错了什么?

Dispose我正在使用的重载文档说:

在所有当前排队的回调完成之前,不会释放计时器。

编辑:看来文档中的此声明可能不正确。有人可以验证吗?

我知道我可以通过在回调和处置代码之间添加一些信号来解决这个问题,正如 Henk Holterman 下面建议的那样,但除非绝对必要,否则我不想这样做。

4

4 回答 4

12

使用此代码

 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 );

几乎可以肯定,您会在定时器处于休眠状态时对其进行处置。

您必须在 Sleep() 之后保护代码以检测 Disposed 计时器。由于没有 IsDisposed 属性,因此快速而肮脏的方法static bool stopping = false;可能会奏效。

于 2012-09-10T15:45:38.090 回答
0

保护回调方法不使用已处理的计时器的可能解决方案:

ManualResetEvent waitHandle = new ManualResetEvent(false);
if (!timer.Dispose(waitHandle) || waitHandle.WaitOne((int)timeout.TotalMilliseconds)
{
    waitHandle.Close();  // Only close when not timeout
}

另请参阅:https ://stackoverflow.com/a/15902261/193178

于 2013-06-04T11:29:33.470 回答
0

如“Windows 上的并发编程”中所述:
创建一个虚拟类 InvalidWaitHandle,继承自 WaitHandle:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Threading;

namespace MyNameSpace
{
    class InvalidWaitHandle : WaitHandle
    {

    }
}

因此,您可以像这样正确处理 System.Threading.Timer:

public static void DisposeTimer()
{
   MyTimer.Dispose(new InvalidWaitHandle());
   MyTimer = null;
}
于 2017-02-10T16:43:01.340 回答
-4

您无需处理计时器即可停止它。您可以调用Timer.Stop()或设置Timer.Enabledfalse,其中任何一个都会停止计时器运行。

于 2012-09-10T15:44:21.103 回答