C#在X秒后执行动作

Rui*_*Rui 13 c# action timer

我想开发一个Windows控制台应用程序,它在给定时间后定期执行一个动作.我已经读过某个计时器类仅适用于Windows窗体应用程序,那么实现我想要的最佳方法是什么?

Mic*_*ael 20

原始消息:我编写了这个示例控制台应用程序(使用C#4.0 [由默认参数指示]).它非常通用,并利用Action委托允许使用Timer类(在System.Threading中)传递您想要执行的片段.At静态类的重载Do方法完成了计算延迟间隔的所有工作.您可以选择通过指定间隔(以毫秒为单位)来重复剪切.您当然可以修改方法以接受TimeSpan的重复间隔.

三年前提供的控制台应用程序示例已经证明非常受寻找这种性质的人们的欢迎.许多问题涉及工作进度管理.我写了一个可以相当简单地实现的新类.它仅作为使用示例提供.对于那些认为我的风格低劣或充满不必要的代码的人来说,这对我来说很好.您可以根据自己的编程风格,实现实践等进行调整.

对类的以下更改使其更有用:

  • 使用Do()方法创建作业的工作方式与旧类相似.提供了另一个可选参数(类型对象),以指定作业的名称.此名称将允许您修改以前安排的作业(继续阅读).
  • 实例化的Timer对象保留在类内部,允许您具有可以按名称单独引用的多个计划任务.
  • 添加了新的处理程序以进行作业管理(使用密钥).它们由SuspendJob(),GetJobFor(),EndJob()ResumeJob方法提供.
  • 静态构造函数订阅AppDomain.CurrentDomain.DomainUnload事件,以便"解构器"在appdomain(应用程序)卸载时处置任何正在运行的作业.
  • 添加了参数检查以确保参数符合预期.
  • Scoped锁定允许您从任何线程添加作业.

什么没改变?

  • 该示例仍然不处理Action委托的结果.如果您需要更好的东西,请实施它.:)

向下滚动以查找该类的新实现.请享用!


**旧的控制台类如下:**

using System;
   using System.Threading;

   namespace ConsoleApplication1
   {
  /// <summary>
  /// Class that manages the execution of tasks sometime in the future.
  /// </summary>
  public static class At
  {
     #region Members

     /// <summary>
     /// Specifies the method that will be fired to execute the delayed anonymous method.
     /// </summary>
     private readonly static TimerCallback timer = new TimerCallback(At.ExecuteDelayedAction);

     #endregion

     #region Methods

     /// <summary>
     /// Method that executes an anonymous method after a delay period.
     /// </summary>
     /// <param name="action">The anonymous method that needs to be executed.</param>
     /// <param name="delay">The period of delay to wait before executing.</param>
     /// <param name="interval">The period (in milliseconds) to delay before executing the anonymous method again (Timeout.Infinite to disable).</param>
     public static void Do(Action action, TimeSpan delay, int interval = Timeout.Infinite)
     {
        // create a new thread timer to execute the method after the delay
        new Timer(timer, action, Convert.ToInt32(delay.TotalMilliseconds), interval);

        return;
     }

     /// <summary>
     /// Method that executes an anonymous method after a delay period.
     /// </summary>
     /// <param name="action">The anonymous method that needs to be executed.</param>
     /// <param name="delay">The period of delay (in milliseconds) to wait before executing.</param>
     /// <param name="interval">The period (in milliseconds) to delay before executing the anonymous method again (Timeout.Infinite to disable).</param>
     public static void Do(Action action, int delay, int interval = Timeout.Infinite)
     {
        Do(action, TimeSpan.FromMilliseconds(delay), interval);

        return;
     }

     /// <summary>
     /// Method that executes an anonymous method after a delay period.
     /// </summary>
     /// <param name="action">The anonymous method that needs to be executed.</param>
     /// <param name="dueTime">The due time when this method needs to be executed.</param>
     /// <param name="interval">The period (in milliseconds) to delay before executing the anonymous method again (Timeout.Infinite to disable).</param>
     public static void Do(Action action, DateTime dueTime, int interval = Timeout.Infinite)
     {
        if (dueTime < DateTime.Now)
        {
           throw new ArgumentOutOfRangeException("dueTime", "The specified due time has already elapsed.");
        }

        Do(action, dueTime - DateTime.Now, interval);

        return;
     }

     /// <summary>
     /// Method that executes a delayed action after a specific interval.
     /// </summary>
     /// <param name="o">The Action delegate that is to be executed.</param>
     /// <remarks>This method is invoked on its own thread.</remarks>
     private static void ExecuteDelayedAction(object o)
     {
        // invoke the anonymous method
        (o as Action).Invoke();

        return;
     }

     #endregion
  }

  class Program
  {
     static void Main(string[] args)
     {
        Console.WriteLine("Time: {0} - started", DateTime.Now);

        // demonstrate that order is irrelevant
        At.Do(() => Console.WriteLine("Time: {0} - Hello World! (after 5s)", DateTime.Now), DateTime.Now.AddSeconds(5));
        At.Do(() => Console.WriteLine("Time: {0} - Hello World! (after 3s)", DateTime.Now), DateTime.Now.AddSeconds(3));
        At.Do(() => Console.WriteLine("Time: {0} - Hello World! (after 1s)", DateTime.Now), DateTime.Now.AddSeconds(1));

        At.Do
        (
           () =>
           {
              // demonstrate flexibility of anonymous methods
              for (int i = 0; i < 10; i++)
              {
                 Console.WriteLine("Time: {0} - Hello World! - i == {1} (after 4s)", DateTime.Now, i);
              }
           },
           TimeSpan.FromSeconds(4)
        );

        // block main thread to show execution of background threads
        Thread.Sleep(100000);

        return;
     }
  }
   }
Run Code Online (Sandbox Code Playgroud)

**新课程提供如下:**

using System;
using System.Linq;
using System.Threading;
using System.Collections.Generic;

namespace Utility
{
   /// <summary>
   /// Class that is designed to execution Action-based anonymous delegates after a specified
   /// interval.  This class also supports repetitive tasks on an interval.
   /// </summary>
   public static class At
   {
      #region Embedded Classes

      /// <summary>
      /// Embedded class definition for common At job periods.
      /// </summary>
      public static class Periods
      {
         #region Members

         /// <summary>
         /// Specifies an object that indicates to not restart.
         /// </summary>
         public static readonly TimeSpan DoNotStart = TimeSpan.FromMilliseconds(-1.0);

         /// <summary>
         /// Specifies an object that indicates to start immediately.
         /// </summary>
         public static readonly TimeSpan Immediately = TimeSpan.FromMilliseconds(0.0);

         /// <summary>
         /// Specifies an interval of one second.
         /// </summary>
         public static readonly TimeSpan SecondsOne = TimeSpan.FromSeconds(1.0);

         /// <summary>
         /// Specifies an interval of five seconds.
         /// </summary>
         public static readonly TimeSpan SecondsFive = TimeSpan.FromSeconds(5.0);

         /// <summary>
         /// Specifies an interval of fifteen seconds.
         /// </summary>
         public static readonly TimeSpan SecondsFifteen = TimeSpan.FromSeconds(15.0);

         /// <summary>
         /// Specifies an interval of thirty seconds.
         /// </summary>
         public static readonly TimeSpan SecondsThirty = TimeSpan.FromSeconds(30.0);

         /// <summary>
         /// Specifies an interval of 100ms.
         /// </summary>
         public static readonly TimeSpan MicroDelay = TimeSpan.FromMilliseconds(100);

         #endregion
      }

      #endregion

      #region Members

      /// <summary>
      /// Specifies an object that can be used for synchronization.
      /// </summary>
      private readonly static object syncRoot;

      /// <summary>
      /// Specifies a collection of Timer object that were created for interval-based execution.
      /// </summary>
      /// <remarks>
      /// We must keep these in a collection to prevent the GC from disposing of the timers.
      /// </remarks>
      private readonly static Dictionary<object, Timer> ActiveTimers;

      /// <summary>
      /// Specifies a collection of timestamps of when timers are created.
      /// </summary>
      private readonly static Dictionary<object, DateTime> TimerCreation;

      /// <summary>
      /// Specifies an object that will produce pseudo-random numbers.
      /// </summary>
      private readonly static Random RNG;

      #endregion

      #region Static Constructor

      static At()
      {
         syncRoot = new object();

         ActiveTimers = new Dictionary<object, Timer>();
         TimerCreation = new Dictionary<object, DateTime>();

         RNG = new Random();

         // "deconstructor"
         AppDomain.CurrentDomain.DomainUnload += new EventHandler(CurrentDomain_DomainUnload);

         return;
      }

      /// <summary>
      /// Method used to cleanup resources used by this object.
      /// </summary>
      static void CurrentDomain_DomainUnload(object sender, EventArgs e)
      {
         // dispose of all the timers directly
         At.ActiveTimers.Values.ToList().ForEach(a => a.Dispose());

         return;
      }

      #endregion

      #region Methods

      #region At Job Staging

      /// <summary>
      /// Method that executes an anonymous method after a delay period.
      /// </summary>
      /// <param name="action">The anonymous method that needs to be executed.</param>
      /// <param name="delay">The period of delay to wait before executing.</param>
      /// <param name="interval">The period (in milliseconds) to delay before executing the anonymous method again (Timeout.Infinite to disable).</param>
      public static Timer Do(Action action, TimeSpan delay, TimeSpan? onInterval = null, object key = null)
      {
         Timer timer;

         if (key == null)
         {
            // auto-generate a key
            key = string.Concat("Auto(", At.RNG.NextNonNegativeLong(), ")");
         }

         lock (At.ActiveTimers)
         {
            // action - contains the method that we wish to invoke
            At.ActiveTimers.Add(key, timer = new Timer(ActionInvoker, action, delay, onInterval ?? At.Periods.DoNotStart));
            At.TimerCreation.Add(key, DateTime.Now);
         }

         //Log.Message
         //(
         //   LogMessageType.Debug,
         //   "[DEBUG] {0}: registered At job (key = {1}, initial delay = {2}, interval = {3})",
         //   action,
         //   key,
         //   delay,
         //   (onInterval == null) ? "never" : onInterval.Value.ToString()
         //);

         return timer;
      }

      /// <summary>
      /// Method that executes an anonymous method after a delay period.
      /// </summary>
      /// <param name="action">The anonymous method that needs to be executed.</param>
      /// <param name="delay">The period of delay (in milliseconds) to wait before executing.</param>
      /// <param name="interval">The period (in milliseconds) to delay before executing the anonymous method again (Timeout.Infinite to disable).</param>
      public static Timer Do(Action action, int delay, int interval = Timeout.Infinite, object key = null)
      {
         return Do(action, TimeSpan.FromMilliseconds(delay), TimeSpan.FromMilliseconds(interval), key);
      }

      /// <summary>
      /// Method that executes an anonymous method after a delay period.
      /// </summary>
      /// <param name="action">The anonymous method that needs to be executed.</param>
      /// <param name="dueTime">The due time when this method needs to be executed.</param>
      /// <param name="interval">The period (in milliseconds) to delay before executing the anonymous method again (Timeout.Infinite to disable).</param>
      public static Timer Do(Action action, DateTime dueTime, int interval = Timeout.Infinite, object key = null)
      {
         if (dueTime < DateTime.Now)
         {
            throw new ArgumentOutOfRangeException("dueTime", "The specified due time has already elapsed.");
         }

         return Do(action, dueTime - DateTime.Now, TimeSpan.FromMilliseconds(interval), key);
      }

      #endregion

      #region At Job Retrieval

      /// <summary>
      /// Method that attempts to retrieve a job (Timer object) for a given key.
      /// </summary>
      /// <param name="key">The key that we are getting a job for.</param>
      public static Timer GetJobFor(object key)
      {
         if (key == null)
         {
            throw new ArgumentNullException("key");
         }

         lock (At.ActiveTimers)
         {
            if (At.ActiveTimers.ContainsKey(key) == false)
            {
               /*
               Log.Message
               (
                  LogMessageType.Error,
                  "[ERROR] At({0}): unable to find a job with specified key",
                  key
               );
               */
               return null;
            }

            return At.ActiveTimers[key];
         }
      }

      /// <summary>
      /// Method that ends a job and removes all resources associated with it.
      /// </summary>
      /// <param name="key">The key that we are getting a job for.</param>
      public static void EndJob(object key)
      {
         Timer timer;

         if ((timer = GetJobFor(key)) == null)
         {
            // no timer - cannot suspend
            return;
         }

         // dispose of the timer object
         timer.Dispose();

         lock (At.ActiveTimers)
         {
            // remove the existence from the dictionary
            At.ActiveTimers.Remove(key);
            /*
            Log.Message
            (
               LogMessageType.Info,
               "[INFO] At({0}): job has been disposed (created {1}, duration {2})",
               key,
               TimerCreation[key].ToISODateTime(),
               (DateTime.Now - TimerCreation[key]).ToText()
            );
            */
            At.TimerCreation.Remove(key);
         }

         return;
      }

      /// <summary>
      /// Method that attempts to suspend an active job (using the provided key).
      /// </summary>
      /// <param name="key">The key that we are getting a job for.</param>
      public static void SuspendJob(object key)
      {
         Timer timer;

         if ((timer = GetJobFor(key)) == null)
         {
            // no timer - cannot suspend
            return;
         }

         // set the timer to not restart
         timer.Change(TimeSpan.FromMilliseconds(-1), TimeSpan.FromMilliseconds(-1));
         /*
         Log.Message
         (
            LogMessageType.Info,
            "[INFO] At({0}): job has been suspended",
            key
         );
         */
         return;
      }

      /// <summary>
      /// Method that attempts to resume an active job (using the provided key).
      /// </summary>
      /// <param name="key">The key that we are getting a job for.</param>
      /// <param name="delay">The amount of delay before restarting the job (specify <b>0</b> to restart immediately).</param>
      /// <param name="interval">The delay between intervals (specify <b>-1ms</b> to prevent intervals).</param>
      public static void ResumeJob(object key, TimeSpan delay, TimeSpan interval)
      {
         Timer timer;

         if ((timer = GetJobFor(key)) == null)
         {
            // no timer - cannot suspend
            return;
         }

         // set the timer to not restart
         timer.Change(delay, interval);
         /*
         Log.Message
         (
            LogMessageType.Info,
            "[INFO] At({0}): job has been resumed (delay = {1}, interval = {2})",
            key,
            delay,
            interval
         );
         */
         return;
      }

      #endregion

      /// <summary>
      /// Method that invokes an action delegate on a timer.
      /// </summary>
      /// <param name="o">A reference to the action that is to be taken.</param>
      private static void ActionInvoker(object o)
      {
         // invoke the delegate
         (o as Action).Invoke();

         return;
      }

      #endregion
   }
}
Run Code Online (Sandbox Code Playgroud)

  • 非常好的概念.Groo提出的问题很容易解决(或微不足道).至于处理定时器 - 我将类改为静态并实现了IDisposable.我在构造函数中分配了计时器而不是"Do"方法,而是在Dispose方法中将其处理掉.谢谢! (3认同)

Ree*_*sey 15

您可以在Console应用程序中使用System.Threading.Timer(或者,System.Timers.Timer,它实际上与System.Threading.Timer相同).它只是您想要避免的Windows窗体或WPF特定计时器.


Ric*_*ein 5

使用Rx你可以这样做:

        var timer = Observable.Interval(TimeSpan.FromSeconds(1));
        timer.Subscribe(l => Console.WriteLine(l));
        Thread.Sleep(Timeout.Infinite);
Run Code Online (Sandbox Code Playgroud)

只是另一种选择.