Is there a Task based replacement for System.Threading.Timer?

前端 未结 7 1000
日久生厌
日久生厌 2020-11-27 09:49

I\'m new to .Net 4.0\'s Tasks and I wasn\'t able to find what I thought would be a Task based replacement or implementation of a Timer, e.g. a periodic Task. Is there such a

7条回答
  •  悲&欢浪女
    2020-11-27 10:13

    UPDATE I am marking the answer below as the "answer" since this is old enough now that we should be using the async/await pattern. No need to downvote this anymore. LOL


    As Amy answered, there is no Tasked based periodic/timer implementation. However, based upon my original UPDATE, we have evolved this into something quite useful and production tested. Thought I would share:

    using System;
    using System.Diagnostics;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApplication7
    {
        class Program
        {
            static void Main(string[] args)
            {
                Task perdiodicTask = PeriodicTaskFactory.Start(() =>
                {
                    Console.WriteLine(DateTime.Now);
                }, intervalInMilliseconds: 2000, // fire every two seconds...
                   maxIterations: 10);           // for a total of 10 iterations...
    
                perdiodicTask.ContinueWith(_ =>
                {
                    Console.WriteLine("Finished!");
                }).Wait();
            }
        }
    
        /// 
        /// Factory class to create a periodic Task to simulate a  using Tasks.
        /// 
        public static class PeriodicTaskFactory
        {
            /// 
            /// Starts the periodic task.
            /// 
            /// The action.
            /// The interval in milliseconds.
            /// The delay in milliseconds, i.e. how long it waits to kick off the timer.
            /// The duration.
            /// If the duration is set to 10 seconds, the maximum time this task is allowed to run is 10 seconds.
            /// The max iterations.
            /// if set to true executes each period in a blocking fashion and each periodic execution of the task
            /// is included in the total duration of the Task.
            /// The cancel token.
            ///  used to create the task for executing the .
            /// A 
            /// 
            /// Exceptions that occur in the  need to be handled in the action itself. These exceptions will not be 
            /// bubbled up to the periodic task.
            /// 
            public static Task Start(Action action,
                                     int intervalInMilliseconds = Timeout.Infinite,
                                     int delayInMilliseconds = 0,
                                     int duration = Timeout.Infinite,
                                     int maxIterations = -1,
                                     bool synchronous = false,
                                     CancellationToken cancelToken = new CancellationToken(),
                                     TaskCreationOptions periodicTaskCreationOptions = TaskCreationOptions.None)
            {
                Stopwatch stopWatch = new Stopwatch();
                Action wrapperAction = () =>
                {
                    CheckIfCancelled(cancelToken);
                    action();
                };
    
                Action mainAction = () =>
                {
                    MainPeriodicTaskAction(intervalInMilliseconds, delayInMilliseconds, duration, maxIterations, cancelToken, stopWatch, synchronous, wrapperAction, periodicTaskCreationOptions);
                };
    
                return Task.Factory.StartNew(mainAction, cancelToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
            }
    
            /// 
            /// Mains the periodic task action.
            /// 
            /// The interval in milliseconds.
            /// The delay in milliseconds.
            /// The duration.
            /// The max iterations.
            /// The cancel token.
            /// The stop watch.
            /// if set to true executes each period in a blocking fashion and each periodic execution of the task
            /// is included in the total duration of the Task.
            /// The wrapper action.
            ///  used to create a sub task for executing the .
            private static void MainPeriodicTaskAction(int intervalInMilliseconds,
                                                       int delayInMilliseconds,
                                                       int duration,
                                                       int maxIterations,
                                                       CancellationToken cancelToken,
                                                       Stopwatch stopWatch,
                                                       bool synchronous,
                                                       Action wrapperAction,
                                                       TaskCreationOptions periodicTaskCreationOptions)
            {
                TaskCreationOptions subTaskCreationOptions = TaskCreationOptions.AttachedToParent | periodicTaskCreationOptions;
    
                CheckIfCancelled(cancelToken);
    
                if (delayInMilliseconds > 0)
                {
                    Thread.Sleep(delayInMilliseconds);
                }
    
                if (maxIterations == 0) { return; }
    
                int iteration = 0;
    
                ////////////////////////////////////////////////////////////////////////////
                // using a ManualResetEventSlim as it is more efficient in small intervals.
                // In the case where longer intervals are used, it will automatically use 
                // a standard WaitHandle....
                // see http://msdn.microsoft.com/en-us/library/vstudio/5hbefs30(v=vs.100).aspx
                using (ManualResetEventSlim periodResetEvent = new ManualResetEventSlim(false))
                {
                    ////////////////////////////////////////////////////////////
                    // Main periodic logic. Basically loop through this block
                    // executing the action
                    while (true)
                    {
                        CheckIfCancelled(cancelToken);
    
                        Task subTask = Task.Factory.StartNew(wrapperAction, cancelToken, subTaskCreationOptions, TaskScheduler.Current);
    
                        if (synchronous)
                        {
                            stopWatch.Start();
                            try
                            {
                                subTask.Wait(cancelToken);
                            }
                            catch { /* do not let an errant subtask to kill the periodic task...*/ }
                            stopWatch.Stop();
                        }
    
                        // use the same Timeout setting as the System.Threading.Timer, infinite timeout will execute only one iteration.
                        if (intervalInMilliseconds == Timeout.Infinite) { break; }
    
                        iteration++;
    
                        if (maxIterations > 0 && iteration >= maxIterations) { break; }
    
                        try
                        {
                            stopWatch.Start();
                            periodResetEvent.Wait(intervalInMilliseconds, cancelToken);
                            stopWatch.Stop();
                        }
                        finally
                        {
                            periodResetEvent.Reset();
                        }
    
                        CheckIfCancelled(cancelToken);
    
                        if (duration > 0 && stopWatch.ElapsedMilliseconds >= duration) { break; }
                    }
                }
            }
    
            /// 
            /// Checks if cancelled.
            /// 
            /// The cancel token.
            private static void CheckIfCancelled(CancellationToken cancellationToken)
            {
                if (cancellationToken == null)
                    throw new ArgumentNullException("cancellationToken");
    
                cancellationToken.ThrowIfCancellationRequested();
            }
        }
    }
    

    Output:

    2/18/2013 4:17:13 PM
    2/18/2013 4:17:15 PM
    2/18/2013 4:17:17 PM
    2/18/2013 4:17:19 PM
    2/18/2013 4:17:21 PM
    2/18/2013 4:17:23 PM
    2/18/2013 4:17:25 PM
    2/18/2013 4:17:27 PM
    2/18/2013 4:17:29 PM
    2/18/2013 4:17:31 PM
    Finished!
    Press any key to continue . . .
    

提交回复
热议问题