C#在X秒后执行动作

时间:2010-09-20 22:39:30

标签: c# timer action

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

5 个答案:

答案 0 :(得分:20)

  

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

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

该课程的以下更改使其更具用:

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

什么没改变?

  • 该示例仍然无法处理操作委托的结果。如果您需要更好的东西,请实施它。 :)

向下滚动以查找该类的新实现。享受!


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

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;
     }
  }
   }

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

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
   }
}

答案 1 :(得分:15)

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

答案 2 :(得分:5)

使用Rx可以执行此操作:

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

只是另一种选择。

答案 3 :(得分:3)

//<Namespace>.Utilities.Extensions
public static class ActionExtensions
{
    public static void RunAfter(this Action action, TimeSpan span)
    {
        var dispatcherTimer = new DispatcherTimer { Interval = span };
        dispatcherTimer.Tick += (sender, args) =>
        {
            var timer = sender as DispatcherTimer;
            if (timer != null)
            {
                timer.Stop();
            }

            action();
        };
        dispatcherTimer.Start();
    }
}

//<Namespace>.Utilities
public static class CommonUtil
{
    public static void Run(Action action, TimeSpan afterSpan)
    {
        action.RunAfter(afterSpan);
    }
}

用法:

CommonUtil.Run(() =>
{
    // some actions
}, TimeSpan.FromMilliseconds(5000));

答案 4 :(得分:0)

定期执行某项操作的最简单方法是使用此类代码

new Timer((Object stateInfo) => { Console.WriteLine("Your Job every 1 sec"); }, new AutoResetEvent(false), 0, 1000);

如果您想延迟(在X秒后执行操作),请将0更改为您的毫秒数。

如果您只需要延迟,请将1000更改为Timeout.Infinite。

当然,你必须阻止线程:

Console.ReadKey();

autoEvent.WaitOne()

如果您明确使用AutoResetEvent