代码之家  ›  专栏  ›  技术社区  ›  Oak

不可重入C#计时器

  •  10
  • Oak  · 技术社区  · 16 年前

    f() 每一个 t 但是如果之前调用 f() 还没做完,等做完再说。

    我读过一些关于可用计时器的文章,但是除了手工编写之外,找不到任何好的方法来做我想做的事情。任何关于如何实现这一点的帮助都将不胜感激,尽管我担心我可能无法找到一个使用计时器的简单解决方案。

    t型 是一秒钟 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)
    

    注意,这个计时器的本质是 f()

    8 回复  |  直到 8 年前
        1
  •  8
  •   PaulG    16 年前

    您可以只使用一个“全局”级别的var(或者更可能是一个公共属性,与 f() f() 正在运行。

    所以如果 f() TimedEvent f() Running 是的

    这样,您的计时器每秒钟启动一次,然后启动计时事件(如果它尚未运行)
    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;   
    }
    
        2
  •  12
  •   Hans Passant    12 年前

        3
  •  3
  •   SLaks    16 年前

    您可以使用非重新启动计时器,然后在方法完成后手动重新启动计时器。

    t 调用间隔时间)

    你可以通过将间隔设置为 lastTick + t - Now ,并立即运行该方法 <= 0 .

    如果你需要停止计时,要当心比赛情况。

        4
  •  1
  •   Jason Williams    16 年前

    你不能让计时器按预定的时间间隔给你打电话。计时器只会给你回电话 不早于

    某些计时器比其他计时器更好(例如,Windows.Forms.Timer与System.Threading.Timer相比非常不稳定和不可靠)

    要停止重新调用计时器,一种方法是在方法运行时停止计时器(根据您使用的计时器类型,您可以在处理程序退出时停止它并再次启动它,或者使用某些计时器,您可以请求单个回调,而不是重复回调,因此处理程序的每次执行只是将下一个调用排成队列)。

    为了保持这些调用之间的时间相对均匀,可以记录自处理程序上次执行以来的时间,并使用该时间计算延迟,直到需要下一个事件。e、 g.如果您希望每秒被调用一次,并且您的计时器在1.02秒时完成processing,那么您可以设置下一个计时器回调,持续时间为0.98秒,以适应您在处理过程中已经“用完”了下一秒的一部分。

        5
  •  0
  •   max    16 年前

    简单的解决方案:

    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);
        }
    }
    
        6
  •  0
  •   code4life    16 年前

    如何使用方法f()的委托,将它们排队到一个堆栈中,并在每个委托完成时弹出堆栈?当然,你还需要计时器。

        7
  •  0
  •   csharptest.net    16 年前

    发生或试图赶上。。。下面是创建线程的简单助手例程。

    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;
    }
    
        8
  •  0
  •   Sudhanshu Mishra    13 年前

    如果您不反对使用已经提供了此类功能的开源库,那么我已经通过使用 Quartz.NET 创建作业并附加触发器时,可以指定如果上一个触发器尚未完成其作业的执行,应执行的操作