Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
不可重入C#计时器_C#_Multithreading_Timer - Fatal编程技术网

不可重入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;
}