3

我有一个 Elapsed 方法,其中有一个 while 循环。如果计时器从另一个线程被禁用/停止,我希望这个循环停止。我可以依赖 Elapsed 方法中计时器的 Enabled 属性,还是应该创建一个“volatile bool timerEnabled”变量来确定。我的测试表明它没问题,但我想在将它投入生产之前确定这一点。

这就是我想要实现的(不是实际代码,而是关闭)

private volatile bool isElapsedAlreadyRunning

void myTimer_Elapsed(object sender, ElapsedEventArgs e)
{
    if (!isElapsedAlreadyRunning)   // to prevent reentrance
    {
        isElapsedAlreadyRunning = true;
        try 
        {
            while (myTimer.Enabled && some other condition)
            {
                do stuff
            }
        }
        finally
        {
            isElapsedAlreadyRunning = false;
        }
    }
}

myTimer.Start() and myTimer.Stop() are in other methods that can be called frrom other threads

我正在使用 System.Timers.Timer 类

如果您有任何其他意见或看到此设计中的任何陷阱,请随时发表评论:)

谢谢


编辑 :

伙计,穿线很难。根据答案和其他stackoverflow问题(特别是这个答案),这将是做到这一点的方法(我希望这次没问题)

public class NoLockTimer : IDisposable
{
    private readonly System.Timers.Timer _timer;

    private bool _isTimerStopped = false;
    private readonly object _isTimerStoppedLock = new object();

    public NoLockTimer()
    {
        _timer = new System.Timers.Timer { AutoReset = false, Interval = 1000 };

        _timer.Elapsed += delegate
        {
            try
            {
                while (!IsTimerStopped && some other condition) 
                {
                    // do stuff
                }
            }
            catch (Exception e)
            {
                // Do some logging
            }
            finally
            {
                if (!IsTimerStopped) 
                {
                    _timer.Start(); // <- Manual restart.
                }
            }
        };

        _timer.Start();
    }

    public void Stop()
    {
        IsTimerStopped = true;
        if (_timer != null)
        {
            _timer.Stop();
        }
    }

    private bool IsTimerStopped
    {
        get 
        {
            lock (_isTimerStoppedLock)
            {
                return _isTimerStopped;
            }
        }
        set 
        {
            lock (_isTimerStoppedLock)
            {
                _isTimerStopped = value;
            }
        }
    }

    public void Dispose()
    {
        Stop();
        if (_timer != null)
        {
            _timer.Dispose();
        }
    }
} 
4

2 回答 2

6

不,这不安全。Elapsed 事件处理程序在线程池线程上调用。您无法预测该线程何时实际调用您的方法,这取决于进程中正在运行的其他 TP 线程。同时进行两个通话在技术上是可行的。这使得 isElapsedAlreadyRunning 变量上的volatile关键字不足以确保该方法是线程安全的,您必须改用lock关键字或 Monitor.TryEnter()。

当您将 Timer 的 AutoReset 属性设置为 false 时,此问题就会消失。确保在 finally 块中重新启动计时器,Timer.Elapsed 事件的另一个令人讨厌的问题是异常在没有诊断的情况下被吞没。System.Threading.Timer 是一个全面的更好的计时器,这样的惊喜更少。

Timer.Enabled 属性也有类似的问题,你总是会看到很晚。

于 2013-01-28T14:36:44.323 回答
1

你的后卫isElapsedAlreadyRunning显然不是线程安全的。

但是您可以简单地用lock(...) { ...}语句替换它。

于 2013-01-28T14:33:40.887 回答