在应用程序/服务关闭/停止之前等待计时器已用事件完成

MrE*_*yes 11 c# timer

简介:在Windows服务和控制台应用程序中,我调用一个包含Timer的公共库,该Timer定期触发一个大约需要30秒才能完成的操作.这很好,但是......

当调用服务停止或应用程序出口并且计时器在ElapsedEventHandler中时,我需要服务停止/应用程序出口等待事件处理程序完成.

我已经通过在调用计时器停止方法时检查布尔InEvent属性来实现此功能.

虽然这很有用,但问题是:这是最好的方法吗?是否有更好的方法可以更好地服务于此目的?

另一个问题是我需要避免服务停止请求因"服务无法响应停止请求"而失败

这是我的实施

public sealed class TimedProcess : IDisposable
{
    static TimedProcess singletonInstance;
    bool InEvent;
    Timer processTimer;

    private TimedProcess()
    {
    }

    public static TimedProcess Instance
    {
        get
        {
            if (singletonInstance == null)
            {
                singletonInstance = new TimedProcess();
            }

            return singletonInstance;
        }
    }

    public void Start(double interval)
    {
        this.processTimer = new Timer();
        this.processTimer.AutoReset = false;
        this.processTimer.Interval = interval;
        this.processTimer.Elapsed += new ElapsedEventHandler(this.processTimer_Elapsed);
        this.processTimer.Enabled = true;
    }

    public void Stop()
    {
        if (processTimer != null)
        {
            while (InEvent)
            {
            }

            processTimer.Stop();
        }
    }

    void processTimer_Elapsed(object sender, ElapsedEventArgs e)
    {
        try
        {
            InEvent = true;
            // Do something here that takes ~30 seconds
        }
        catch
        {
        }
        finally
        {
            InEvent = false;
            processTimer.Enabled = true;
        }
    }

    public void Dispose()
    {
        if (processTimer != null)
        {
            Stop();
            processTimer.Dispose();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这就是在服务OnStart/console应用程序main中调用它的方式:

TimedProcess.Instance.Start(1000);
Run Code Online (Sandbox Code Playgroud)

这是在服务OnStop和应用程序main(挂起的按键)中调用它的方式:

TimedProcess.Instance.Stop();
Run Code Online (Sandbox Code Playgroud)

Jim*_*hel 10

可能最简单,最可靠的方法是使用Monitor.创建主程序和计时器回调可以访问的对象:

private object _timerLock = new object();
Run Code Online (Sandbox Code Playgroud)

您的主程序在关闭之前尝试锁定它:

// wait for timer process to stop
Monitor.Enter(_timerLock);
// do shutdown tasks here
Run Code Online (Sandbox Code Playgroud)

你的计时器回调也会锁定它:

void processTimer_Elapsed(object sender, ElapsedEventArgs e)
{
    if (!Monitor.TryEnter(_timerLock))
    {
        // something has the lock. Probably shutting down.
        return;
    }
    try
    {
        // Do something here that takes ~30 seconds
    }
    finally
    {
        Monitor.Exit(_timerLock);
    }
}
Run Code Online (Sandbox Code Playgroud)

一旦获得锁,主程序就不应该释放锁.

如果您希望主程序在一段时间后继续关闭,无论是否获得锁定,请使用Monitor.TryEnter.例如,这将等待15秒.

bool gotLock = Monitor.TryEnter(_timerLock, TimeSpan.FromSeconds(15));
Run Code Online (Sandbox Code Playgroud)

返回值true是否能够获得锁定.

顺便说一句,我强烈建议你使用System.Threading.Timer而不是System.Timers.Timer.后者压制异常,最终可能会隐藏错误.如果您的Elapsed事件发生异常,它将永远不会逃脱,这意味着您永远不会知道它.有关更多信息,请参阅我的博文.