Best way to create a “run-once” time delayed function in C#

前端 未结 12 1415
我在风中等你
我在风中等你 2020-12-13 08:54

I am trying to create a function that takes in an Action and a Timeout, and executes the Action after the Timeout. The function is to be non-blocking. The function must be

12条回答
  •  时光说笑
    2020-12-13 09:13

    There is nothing built-in to .Net 4 to do this nicely. Thread.Sleep or even AutoResetEvent.WaitOne(timeout) are not good - they will tie up thread pool resources, I have been burned trying this!

    The lightest weight solution is to use a timer - particularly if you will have many tasks to throw at it.

    First make a simple scheduled task class:

    class ScheduledTask
    {
        internal readonly Action Action;
        internal System.Timers.Timer Timer;
        internal EventHandler TaskComplete;
    
        public ScheduledTask(Action action, int timeoutMs)
        {
            Action = action;
            Timer = new System.Timers.Timer() { Interval = timeoutMs };
            Timer.Elapsed += TimerElapsed;            
        }
    
        private void TimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            Timer.Stop();
            Timer.Elapsed -= TimerElapsed;
            Timer = null;
    
            Action();
            TaskComplete(this, EventArgs.Empty);
        }
    }
    

    Then, create a scheduler class - again, very simple:

    class Scheduler
    {        
        private readonly ConcurrentDictionary _scheduledTasks = new ConcurrentDictionary();
    
        public void Execute(Action action, int timeoutMs)
        {
            var task = new ScheduledTask(action, timeoutMs);
            task.TaskComplete += RemoveTask;
            _scheduledTasks.TryAdd(action, task);
            task.Timer.Start();
        }
    
        private void RemoveTask(object sender, EventArgs e)
        {
            var task = (ScheduledTask) sender;
            task.TaskComplete -= RemoveTask;
            ScheduledTask deleted;
            _scheduledTasks.TryRemove(task.Action, out deleted);
        }
    }
    

    It can be called as follows - and is very lightweight:

    var scheduler = new Scheduler();
    
    scheduler.Execute(() => MessageBox.Show("hi1"), 1000);
    scheduler.Execute(() => MessageBox.Show("hi2"), 2000);
    scheduler.Execute(() => MessageBox.Show("hi3"), 3000);
    scheduler.Execute(() => MessageBox.Show("hi4"), 4000);
    

提交回复
热议问题