System.Timers.Timer.Enabled属性是否安全,因此可以从计时器的Elapsed事件中访问它?

时间:2013-01-28 14:27:18

标签: c# multithreading timer thread-safety

我有一个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问题(this answer particularly),这将是这样做的方式(我希望这次没关系)

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

2 个答案:

答案 0 :(得分:6)

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

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

Timer.Enabled属性有类似的问题,你总会看到它迟到。

答案 1 :(得分:1)

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

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