我想要一个具有以下属性的计时器:
无论调用start多少次,只有一个回调线程正在运行
关于间隔,忽略了回调函数所花费的时间。例如,如果间隔为100毫秒并且回叫需要4000毫秒才能执行,则回调将在100毫秒,4100毫秒等时调用。
我看不到任何可用内容,所以编写了以下代码。有一个更好的方法吗?
/**
* 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();
}
}
}
答案 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提供四个计时器。其中两个是通用多线程 定时器:
另外两个是特殊用途的单线程计时器:
最后2个旨在消除WPF和Windows窗体应用程序的线程安全问题。
例如,在计时器中使用 WebBrowser 来捕获网页中的屏幕截图需要是单线程的,并且如果它位于另一个线程上,则会在运行时出错。
单线程计时器具有以下优点
主要缺点要注意
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
编辑:间距