簡體   English   中英

使用易失性布爾創建非重入“非暫停”計時器

[英]Creating a Non-Reentrant “Non-Pausing” Timer using a volatile bool

我已經看到了很多示例( 在這里和其他地方),通過在調用經過的處理程序方法時停止計時器,然后在經過的處理程序方法結束時再次啟動計時器,來創建非重入計時器。 這似乎是推薦的方法。 這種方法的問題是,在運行“經過的處理程序方法”時,您會在時間上有間隔。 您最終可能會在很短的時間內造成大量時間偏離。

因此,我正在考慮一種更好的方法,我可以使用布爾值來確定Timer的狀態,並且不管Elapsed Handler當前是否正在運行,是否正在運行,那么對Elapsed Handler的調用就是立即返回,其余未執行。

以下是基本思路

volatile bool _IsProcessingElapsedMethod = false;
private void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
    if (_IsProcessingElapsedMethod)
    {
        Console.WriteLine("Warning: Re-Entrance was attempted and Ignored.");
        return;
    }
    _IsProcessingElapsedMethod = true;

    //** DO Something here

    _IsProcessingElapsedMethod = false;
}

一定有一個原因,我從未見過有人這樣做。 我是否缺少一些明顯的陷阱? 這似乎是一個非常簡單的解決方案。

下面是一個可編譯的示例。

using System;
using System.Threading.Tasks;
using System.Timers;

namespace QuestionNon_ReEntrantTimer
{
    class Program
    {
        static private int Timer1_ElapsedCount = 1;

        static void Main(string[] args)
        {
            NonReEntrantTimer timer1 = new NonReEntrantTimer(500);
            timer1.Elapsed += Timer1_Elapsed;
            timer1.Start();
            Console.WriteLine("Press Any key to Exit");
            Console.ReadKey();
        }

        private static void Timer1_Elapsed(object sender, ElapsedEventArgs e)
        {

            int delayTime;

            if(Timer1_ElapsedCount < 10)
            {
                delayTime = 300 * Timer1_ElapsedCount++;
            }
            else
            {
                Timer1_ElapsedCount++;
                delayTime = 400;
            }

            Console.WriteLine($"Timer1_Elapsed Call Count is {Timer1_ElapsedCount} Waiting for {delayTime} ms");
            Task.Delay(delayTime).Wait();

        }
    }


    public class NonReEntrantTimer : IDisposable
    {
        Timer _timer = new Timer();

        public event ElapsedEventHandler Elapsed;

        volatile bool _IsProcessingElapsedMethod = false;

        public NonReEntrantTimer(double interval)
        {
            _timer = new Timer(interval);
            _timer.Elapsed += _timer_Elapsed;
        }

        public void Start() => _timer.Start();

        public void Stop() => _timer.Stop();

        public void Close() => _timer.Close();

        private void _timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (_IsProcessingElapsedMethod)
            {
                Console.WriteLine("Warning: Re-Entrance was attempted and Ignored.");
                return;
            }
            _IsProcessingElapsedMethod = true;

            Elapsed?.Invoke(sender, e);

            _IsProcessingElapsedMethod = false;
        }

        public void Dispose()
        {
            _timer.Dispose();
        }
    }
}

我會提出這種簡單的異步模式。 它每ts執行一次給定的Action ,但是開始當前迭代之前 ,開始倒計時到下一次執行。 如果執行花費的時間比ts ,則將下一次迭代推遲到上一次完成之后。

async Task ExecuteEvery(TimeSpan ts, Action a, CancellationToken ct)
{
    try
    {
        var currentDelay = Task.Delay(ts, ct);
        while (!ct.IsCancellationRequested)
        {
            await currentDelay; // waiting for the timeout
            currentDelay = Task.Delay(ts, ct); // timeout finished, starting next wait
            a(); // executing action in the meanwhile
        }
    }
    catch (OperationCanceledException) when (ct.IsCancellationRequested)
    {
        // if we are cancelled, nothing to do, just exit
    }
}

您可以通過取消令牌來停止迭代。 您可以通過使用Task.Run啟動操作,將操作執行卸載到線程池中。


更新:如果您希望計時器在緩慢的動作后嘗試趕上,則可以進行一些小的更改:

async Task ExecuteEvery(TimeSpan ts, Action a, CancellationToken ct)
{
    try
    {
        for (var targetTime = DateTime.Now + ts; !ct.IsCancellationRequested; targetTime += ts)
        {
            var timeToWait = targetTime - DateTime.Now;
            if (timeToWait > TimeSpan.Zero)
                await Task.Delay(timeToWait, ct);
            a();
        }
    }
    catch (OperationCanceledException) when (ct.IsCancellationRequested)
    {
        // if we are cancelled, nothing to do, just exit
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM