2010-09-20 32 views
13

Tôi muốn phát triển một ứng dụng bảng điều khiển cửa sổ thực hiện một hành động định kỳ sau một thời gian nhất định. Tôi đã đọc ở đâu đó rằng một lớp timer chỉ có sẵn cho các ứng dụng biểu mẫu windows, vậy cách tốt nhất để hoàn thành những gì tôi muốn là gì?C# thực thi hành động sau X giây

Trả lời

15

Bạn có thể sử dụng System.Threading.Timer (hoặc, cách khác, System.Timers.Timer, có hiệu quả giống như System.Threading.Timer) trong ứng dụng Bảng điều khiển. Nó chỉ là Windows Forms hoặc WPF cụ thể giờ bạn muốn tránh.

5

Sử dụng Rx bạn có thể làm điều này:

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

Chỉ cần một lựa chọn.

20

Thông điệp gốc: Tôi đã viết ứng dụng bảng điều khiển mẫu này (sử dụng C# 4.0 [như được chỉ định bởi các tham số mặc định]). Nó cực kỳ linh hoạt và sử dụng ủy quyền Hành động để cho phép chuyển các đoạn mà bạn muốn thực hiện bằng cách sử dụng lớp Timer (trong System.Threading). Phương thức quá tải làm của Tại static lớp thực hiện tất cả các công việc tính toán khoảng thời gian trễ. Bạn có thể tùy chọn lặp lại bằng cách chỉ định khoảng thời gian (trong mili giây). Tất nhiên bạn có thể sửa đổi phương thức để chấp nhận TimeSpan cho khoảng thời gian lặp lại.

Ví dụ về ứng dụng bảng điều khiển được cung cấp ba năm trước đây đã được chứng minh khá phổ biến cho những người tìm kiếm điều gì đó có tính chất này. Nhiều câu hỏi được hỏi về quản lý lịch biểu công việc. Tôi lấy một vết nứt khi viết một lớp mới có thể được thực hiện khá đơn giản. Nó chỉ được cung cấp như một ví dụ về sử dụng. Đối với những người tin rằng phong cách của tôi là kém hơn hoặc riddled với mã không cần thiết, đó là tốt của tôi. Đó là tùy thuộc vào bạn để tinh chỉnh nó cho phong cách lập trình của bạn, thực hành thực hiện vv

Những thay đổi sau vào lớp làm cho nó có thể sử dụng hơn:

  • Tạo công ăn việc làm với Đỗ() phương pháp hoạt động tương tự như lớp cũ. Một thông số tùy chọn bổ sung là khóa (loại đối tượng) đã được cung cấp để cho phép bạn chỉ định tên công việc. Tên này sẽ cho phép bạn sửa đổi các công việc đã lên lịch trước đó (tiếp tục đọc).
  • Đã khởi tạo Bộ đếm thời gian đối tượng được giữ trong nội bộ trong lớp cho phép bạn có nhiều tác vụ theo lịch có thể được tham chiếu theo từng tên riêng lẻ.
  • Trình xử lý mới đã được thêm vào để quản lý công việc (sử dụng khóa). Họ được cung cấp bởi SuspendJob(), GetJobFor(), EndJob()ResumeJob phương pháp.
  • Nhà xây dựng tĩnh đăng ký sự kiện AppDomain.CurrentDomain.DomainUnload cho "deconstructor" để xử lý bất kỳ công việc đang chạy nào khi appdomain (ứng dụng) đang tải.
  • Kiểm tra tham số đã được thêm vào để đảm bảo thông số là những gì được mong đợi.
  • Khóa phạm vi cho phép bạn thêm công việc từ bất kỳ chuỗi nào.

Điều gì đã không thay đổi?

  • Ví dụ vẫn không xử lý kết quả của đại biểu Hành động. Nếu bạn cần một cái gì đó tốt hơn, hãy thực hiện nó. :)

Cuộn xuống để tìm triển khai mới của lớp học. Thưởng thức!


** Giao diện điều khiển lớp cũ như sau: **

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

** Các lớp mới được quy định như sau: **

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

địa ngục! điều này sẽ có nhiều lượt thích hơn! –

+1

Nó sẽ phục vụ mục đích của nó, nhưng tôi sẽ không sử dụng nó trong mã sản xuất dưới dạng này: đầu tiên, bạn không vứt bỏ bộ hẹn giờ của bạn, nghĩa là chúng được giữ bởi bộ đếm giờ của chúng, ngăn không cho GC xử lý và thu thập chúng. Mỗi lần bạn gọi phương thức 'Do', bạn đang tạo một thể hiện không thể thu thập mới. Các vấn đề khác có liên quan nhiều hơn đến thực hành lập trình tốt: bạn không phải kết thúc mỗi phương thức với một 'return',' ExecuteDelayedAction' trống không nên lấy một 'đối tượng' làm tham số để tránh truyền, và' timer' là một bit lựa chọn đặt tên nghèo cho một cái gì đó mà không thực sự là một bộ đếm thời gian. – Groo

+0

Làm thế nào tôi có thể vứt bỏ bộ hẹn giờ? bạn có thể sửa đổi mã này để giải quyết vấn đề đó không? –

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

Cách sử dụng:

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

'DispatcherTimer' có thể được thay thế bằng' System.Windows.Forms.Timer' và nó hoạt động trong WinForms. –

0

Cách đơn giản nhất để làm điều gì đó kỳ là mã sử dụng như thế này

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

Nếu bạn muốn thực hiện một sự chậm trễ (thực hiện hành động sau X giây), thay đổi từ 0 đến số điện thoại của mili giây.

Nếu bạn chỉ cần chậm trễ, hãy thay đổi 1000 thành Thời gian chờ.Infinite.

Tất nhiên, bạn phải chặn chủ đề:

Console.ReadKey(); 

hoặc

autoEvent.WaitOne() 

nếu bạn sử dụng một cách rõ ràng AutoResetEvent

Các vấn đề liên quan