【问题标题】:Calling System.Timers.Timer.Stop from the timer thread从计时器线程调用 System.Timers.Timer.Stop
【发布时间】:2016-02-03 16:52:53
【问题描述】:

在以下 C# 代码中,System.Timers.Timer.Stop 从计时器线程调用,但也从 UI 线程调用:

private void StartTimer()
{
    // This is in the context of the UI thread.
    this.m_timer = new System.Timers.Timer(1000);

    this.m_timer.Elapsed += (sender, e) =>
    {
        // This is in the context of the timer thread.

        try
        {
            this.m_timer.Stop();

            // Do some stuff.

            this.m_timer.Start();
        }
        catch (Exception exception)
        {
            CTrace.Exception(this, "StartTimer", exception);
        }
    };
    this.m_timer.Start();
}
// After returning from the StartTimer() method, this.m_timer.Stop() 
// will be called from the UI thread.

从定时器线程调用System.Timers.Timer.StartSystem.Timers.Timer.Stop 安全吗?它是线程安全的吗? 谢谢!

【问题讨论】:

  • 是的,this.m_timer.Stop()可以从Elapsed回调的线程中调用,没关系。需要注意的一件事是,如果“// Do some stuff”代码与 UI 交互,那么您必须在 UI 线程上调用/BeginInvoke。
  • 它是线程安全的,没有任何东西会爆炸和冒烟。实际上,确保 Elapsed 事件不再运行通常是不可能的,它可能已被安排在调用 Stop() 之前一微秒在线程池线程上运行。当然,您的将再次启用计时器,因此它将无法正常工作。改用 System.Threading.Timer,它的 Dispose(WaitHandle) 重载提供了互锁保证。
  • @HansPassant 我不同意,两次调用Stop() 与设置两次Enabled = false 相同。如果两个线程同时在if(timer != null) 块中但有1+ 行分开,this line 是否应该抛出空引用异常?

标签: c# multithreading timer


【解决方案1】:

不,Stop()Start() 方法未在 MSDN 的文档中列为线程安全的。

为了最安全,您应该在使用多个线程时锁定所有未在 MSDN 中列为线程安全的操作。

但是您的代码还有其他问题,您在启动新计时器时替换了this.m_timer,但是在计时器回调代码中您也使用this.m_timer,这意味着如果您启动一个计时器然后启动第二个计时器的回调将使用相同的m_timer。您应该只使用一个计时器,而不是在 start 函数中创建它。

除了手动停止将AutoReset 设置为false 的计时器之外,这还为您提供了您尝试自动执行的行为。

private object m_timerLock;
private bool m_timerRunning = false;

public YourClass()
{
    this.m_timer = new System.Timers.Timer(1000);
    this.m_timer.AutoReset = false;
    this.m_timer.Elapsed += TimerCallback;
}

private void StartTimer()
{
    // This is in the context of the UI thread.
    lock(this.m_timerLock)
    {
        this.m_timerRunning = true;
        this.m_timer.Start();
    }
}

private void TimerCallback(object sender, EventArgs e)
{
    // This is in the context of the timer thread.

    try
    {
        //This line is not needed anymore because of the this.m_timer.AutoReset = false.
        //this.m_timer.Stop();

        // Do some stuff.

        lock(this.m_timerLock)
        {
            //This checks to see if StopTimer() was called while the callback was running.
            if(this.m_timerRunning)
                this.m_timer.Start();
        }
    }
    catch (Exception exception)
    {
        CTrace.Exception(this, "StartTimer", exception);
    }
}

public void StopTimer()
{
    lock(this.m_timerLock)
    {
        this.m_timer.Stop();
        this.m_timerRunning = false;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多