不可重入C#计时器
我正在尝试每不可重入C#计时器,c#,multithreading,timer,C#,Multithreading,Timer,我正在尝试每t一次调用一个方法f(),但是如果上一次调用f()尚未完成,请等待它完成 我读过一些关于可用计时器的文章,但除了手工编写之外,找不到任何好的方法来做我想做的事情。任何关于如何实现这一点的帮助都将不胜感激,尽管我担心我可能无法找到使用计时器的简单解决方案 为了澄清,如果t是1秒,并且f()运行我在下面写的任意持续时间,那么: Step Operation Time taken 1 wait 1s 2 f() 0.6s 3
t
一次调用一个方法f()
,但是如果上一次调用f()
尚未完成,请等待它完成
我读过一些关于可用计时器的文章,但除了手工编写之外,找不到任何好的方法来做我想做的事情。任何关于如何实现这一点的帮助都将不胜感激,尽管我担心我可能无法找到使用计时器的简单解决方案
为了澄清,如果t
是1秒,并且f()
运行我在下面写的任意持续时间,那么:
Step Operation Time taken
1 wait 1s
2 f() 0.6s
3 wait 0.4s (because f already took 0.6 seconds)
4 f() 10s
5 wait 0s (we're late)
6 f() 0.3s
7 wait 0.7s (we can disregard the debt from step 4)
请注意,此计时器的性质是,在重新进入时不需要安全,并且大小为1的线程池在这里就足够了。您可以只使用一个“全局”级别的变量(或者更可能是与
f()
在同一类中的公共属性),如果f()
已经运行,它将返回true
因此,如果f()
在名为TimedEvent
的类中,那么f()
要做的第一件事就是将设置为Running
true
这样,计时器每秒启动一次,如果计时事件尚未运行,则启动该事件if(!timedEvent.Running)timedEvent.f()
您评论说,f()
如果花费的时间超过计时器间隔,则不会立即重复。这是一个公平的观点。我可能会在f()
中包含这样的逻辑,以便运行时保持正确。所以它看起来像这样:
public void f(int t) // t is interval in seconds
{
this.running = true;
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
do
{
stopwatch.Reset();
// Do work here
} while (stopWatch.Elapsed.Seconds > t); // repeat if f() took longer than t
this.running = false;
}
您可以使用非重新启动计时器,然后在方法完成后手动重新启动计时器
请注意,这将导致时间安排与您要求的有所不同。(调用之间总是有t
时间间隔)
您可以通过将时间间隔设置为lastTick+t-Now
,并在的情况下立即运行该方法来解决此问题,因为您无法获得一个计时器以精确的预定时间间隔给您打电话。计时器所做的就是在要求的时间之前给你回电话
某些计时器优于其他计时器(例如,与System.Threading.Timer相比,Windows.Forms.Timer非常不稳定且不可靠)
要停止重新调用计时器,一种方法是在方法运行时停止计时器。(根据您使用的计时器类型,您可以在处理程序退出时停止它并再次启动它,或者使用某些计时器,您可以请求单个回调而不是重复回调,因此处理程序的每次执行只会将下一个调用排入队列)
为了在这些调用之间保持时间相对均衡,您可以记录自处理程序上次执行以来的时间,并使用该时间计算延迟,直到需要下一个事件。e、 g.如果您希望每秒调用一次,并且您的计时器在1.02秒时完成验证,那么您可以设置下一个计时器回调,持续时间为0.98秒,以适应您在处理过程中已经“用完”了下一秒的一部分这一事实。使用System.Threading.timer。用一段超时时间初始化它。无限,所以它就像一个一次性计时器。f()完成后,调用其Change()方法重新充电。一个简单的解决方案:
private class Worker : IDisposable
{
private readonly TimeSpan _interval;
private WorkerContext _workerContext;
private sealed class WorkerContext
{
private readonly ManualResetEvent _evExit;
private readonly Thread _thread;
private readonly TimeSpan _interval;
public WorkerContext(ParameterizedThreadStart threadProc, TimeSpan interval)
{
_evExit = new ManualResetEvent(false);
_thread = new Thread(threadProc);
_interval = interval;
}
public ManualResetEvent ExitEvent
{
get { return _evExit; }
}
public TimeSpan Interval
{
get { return _interval; }
}
public void Run()
{
_thread.Start(this);
}
public void Stop()
{
_evExit.Set();
}
public void StopAndWait()
{
_evExit.Set();
_thread.Join();
}
}
~Worker()
{
Stop();
}
public Worker(TimeSpan interval)
{
_interval = interval;
}
public TimeSpan Interval
{
get { return _interval; }
}
private void DoWork()
{
/* do your work here */
}
public void Start()
{
var context = new WorkerContext(WorkThreadProc, _interval);
if(Interlocked.CompareExchange<WorkerContext>(ref _workerContext, context, null) == null)
{
context.Run();
}
else
{
context.ExitEvent.Close();
throw new InvalidOperationException("Working alredy.");
}
}
public void Stop()
{
var context = Interlocked.Exchange<WorkerContext>(ref _workerContext, null);
if(context != null)
{
context.Stop();
}
}
private void WorkThreadProc(object p)
{
var context = (WorkerContext)p;
// you can use whatever time-measurement mechanism you want
var sw = new System.Diagnostics.Stopwatch();
int sleep = (int)context.Interval.TotalMilliseconds;
while(true)
{
if(context.ExitEvent.WaitOne(sleep)) break;
sw.Reset();
sw.Start();
DoWork();
sw.Stop();
var time = sw.Elapsed;
if(time < _interval)
sleep = (int)(_interval - time).TotalMilliseconds;
else
sleep = 0;
}
context.ExitEvent.Close();
}
public void Dispose()
{
Stop();
GC.SuppressFinalize(this);
}
}
私有类工作程序:IDisposable
{
专用只读时间间隔;
私人WorkerContext_WorkerContext;
私有密封类WorkerContext
{
私有只读手册重置事件_evExit;
私有只读线程_线程;
专用只读时间间隔;
公共WorkerContext(ParameterizedThreadStart threadProc,TimeSpan interval)
{
_evExit=新手动重置事件(假);
_线程=新线程(threadProc);
_间隔=间隔;
}
公共手册重置事件退出事件
{
获取{return\u evExit;}
}
公共时间间隔
{
获取{return\u interval;}
}
公开募捐
{
_线程。启动(此);
}
公共停车场()
{
_evExit.Set();
}
公共void StopAndWait()
{
_evExit.Set();
_thread.Join();
}
}
~Worker()
{
停止();
}
公共工作者(时间间隔)
{
_间隔=间隔;
}
公共时间间隔
{
获取{return\u interval;}
}
私房
{
/*你在这里工作吗*/
}
公开作废开始()
{
var context=newworkercontext(WorkThreadProc,_interval);
if(Interlocked.compareeexchange(ref\u workerContext,context,null)==null)
{
context.Run();
}
其他的
{
context.ExitEvent.Close();
抛出新的InvalidOperationException(“工作状态”);
}
}
公共停车场()
{
var context=Interlocked.Exchange(ref\u workerContext,null);
if(上下文!=null)
{
context.Stop();
}
}
私有void WorkThreadProc(对象p)
{
var context=(WorkerContext)p;
//你可以使用任何你想要的时间测量机制
var sw=新系统.Diagnostics.Stopwatch();
int sleep=(int)context.Interval.totalmillizes;
while(true)
{
if(context.ExitEvent.WaitOne(sleep))中断;
sw.Reset();
sw.Start();
销钉();
sw.Stop();
var时间=经过的软件时间;
如果(时间<\u间隔)
sleep=(int)(_interval-time).total毫秒;
其他的
睡眠=0;
}
context.ExitEvent.Close();
}
公共空间处置()
{
停止();
总干事(本);
}
}
如何使用方法f()的委托,将它们排队到堆栈中,并在每个委托完成时弹出堆栈?你还需要提姆
public static Thread StartTimer(TimeSpan interval, Func<bool> operation)
{
Thread t = new Thread(new ThreadStart(
delegate()
{
DateTime when = DateTime.Now;
TimeSpan wait = interval;
while (true)
{
Thread.Sleep(wait);
if (!operation())
return;
DateTime dt = DateTime.Now;
when += interval;
while (when < dt)
when += interval;
wait = when - dt;
}
}
));
t.IsBackground = true;
t.Start();
return t;
}