c#单线程计时器

时间:2011-03-31 09:04:39

标签: c# multithreading timer

我想要一个具有以下属性的计时器:

  1. 无论调用start多少次,只有一个回调线程正在运行

  2. 关于间隔,忽略了回调函数所花费的时间。例如,如果间隔为100毫秒并且回叫需要4000毫秒才能执行,则回调将在100毫秒,4100毫秒等时调用。

  3. 我看不到任何可用内容,所以编写了以下代码。有一个更好的方法吗?

    /**
     * Will ensure that only one thread is ever in the callback
     */
    public class SingleThreadedTimer : Timer
    {
        protected static readonly object InstanceLock = new object();
    
        //used to check whether timer has been disposed while in call back
        protected bool running = false;
    
        virtual new public void Start()
        {
            lock (InstanceLock)
            {
                this.AutoReset = false;
                this.Elapsed -= new ElapsedEventHandler(SingleThreadedTimer_Elapsed);
                this.Elapsed += new ElapsedEventHandler(SingleThreadedTimer_Elapsed);
                this.running = true;
                base.Start();
            }
    
        }
    
        virtual public void SingleThreadedTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            lock (InstanceLock)
            {
                DoSomethingCool();
    
                //check if stopped while we were waiting for the lock, we don't want to restart if this is the case..
                if (running)
                {
                    this.Start();
                }
            }
        }
    
        virtual new public void Stop()
        {
            lock (InstanceLock)
            {
                running = false;
                base.Stop();
            }
        }
    }
    

4 个答案:

答案 0 :(得分:7)

这是一个我刚刚敲门的快速例子;

using System.Threading;
//...
public class TimerExample
{
    private System.Threading.Timer m_objTimer;
    private bool m_blnStarted;
    private readonly int m_intTickMs = 1000;
    private object m_objLockObject = new object();

    public TimerExample()
    {
        //Create your timer object, but don't start anything yet
        m_objTimer = new System.Threading.Timer(callback, m_objTimer, Timeout.Infinite, Timeout.Infinite);
    }

    public void Start()
    {
        if (!m_blnStarted)
        {
            lock (m_objLockObject)
            {
                if (!m_blnStarted) //double check after lock to be thread safe
                {
                    m_blnStarted = true;

                    //Make it start in 'm_intTickMs' milliseconds, 
                    //but don't auto callback when it's done (Timeout.Infinite)
                    m_objTimer.Change(m_intTickMs, Timeout.Infinite);
                }
            }
        }
    }

    public void Stop()
    {
        lock (m_objLockObject)
        {
            m_blnStarted = false;
        }
    }

    private void callback(object state)
    {
        System.Diagnostics.Debug.WriteLine("callback invoked");

        //TODO: your code here
        Thread.Sleep(4000);

        //When your code has finished running, wait 'm_intTickMs' milliseconds
        //and call the callback method again, 
        //but don't auto callback (Timeout.Infinite)
        m_objTimer.Change(m_intTickMs, Timeout.Infinite);
    }
}

答案 1 :(得分:1)

查看[ThreadStatic]属性和.Net 4.0 ThreadLocal泛型类型。这可能会很快为您提供一种编码方式,而不会弄乱线程锁定等。

你的时间类中可能有一个堆栈,你可以实现一个返回IDisposable的Monitor()方法,所以你可以像这样使用定时器:

using (_threadTimer.Monitor())
{
     // do stuff
}

在Dispose()期间,让计时器监视器弹出堆栈的间隔时间戳。

如上所述,手动编码所有锁定和线程识别是一种选择。但是,锁定会影响使用的时间,很可能不仅仅是使用ThreadLocal

为每个线程初始化一个实例

如果你有兴趣,我可能会稍后再举一个例子

答案 2 :(得分:1)

.NET Framework提供四个计时器。其中两个是通用多线程 定时器:

  • System.Threading.Timer
  • System.Timers.Timer

另外两个是特殊用途的单线程计时器

  • System.Windows.Forms.Timer(Windows窗体计时器)
  • System.Windows.Threading.DispatcherTimer(WPF计时器)

最后2个旨在消除WPF和Windows窗体应用程序的线程安全问题

例如,在计时器中使用 WebBrowser 来捕获网页中的屏幕截图需要是单线程的,并且如果它位于另一个线程上,则会在运行时出错。

单线程计时器具有以下优点

  • 你可以忘记线程安全。
  • 在上一个Tick完成之前,新的Tick将永远不会触发 处理
  • 您可以直接更新用户界面元素和控件 勾选事件处理代码,而不调用Control.BeginInvoke或 Dispatcher.BeginIn voke。

主要缺点要注意

  • 一个线程服务所有计时器 - 以及处理UI事件。 这意味着Tick事件处理程序必须快速执行, 否则用户界面就会没有响应。

source:大多数是 C#in a Nutshell 一书中的碎片 - >第22章 - >高级线程 - >计时器 - >单线程定时器

答案 3 :(得分:1)

对于需要单线程计时器并希望在任务完成后计算间隔的任何人。 System.Timers.Timer可以在不锁定或[ThreadStatic]

的情况下完成操作
System.Timers.Timer tmr;

void InitTimer(){
    tmr = new System.Timers.Timer();
    tmr.Interval = 300;
    tmr.AutoReset = false;
    tmr.Elapsed += OnElapsed;
}

void OnElapsed( object sender, System.Timers.ElapsedEventArgs e )
{
    backgroundWorking();

    // let timer start ticking
    tmr.Enabled = true;
}

贷记到Alan N 来源https://www.codeproject.com/Answers/405715/System-Timers-Timer-single-threaded-usage#answer2

编辑:间距