Làm cách nào để thêm bộ hẹn giờ vào ứng dụng bảng điều khiển C #


132

Chỉ thế này - Làm thế nào để bạn thêm bộ hẹn giờ vào ứng dụng bảng điều khiển C #? Sẽ thật tuyệt nếu bạn có thể cung cấp một số ví dụ về mã hóa.


16
Chú ý: các câu trả lời ở đây có lỗi, đối tượng Timer sẽ nhận rác được thu thập. Tham chiếu đến bộ định thời phải được lưu trữ trong một biến tĩnh để đảm bảo nó tiếp tục đánh dấu.
Hans Passant

@HansPassant Bạn dường như đã bỏ lỡ tuyên bố rõ ràng trong câu trả lời của tôi: "Bạn cũng nên luôn luôn sử dụng Hệ thống tĩnh (được chia sẻ trong VB.NET) .Threading.Timer nếu bạn đang phát triển Dịch vụ Windows và yêu cầu hẹn giờ để chạy định kỳ . Điều này sẽ tránh việc thu gom rác quá sớm của đối tượng hẹn giờ của bạn. " Nếu mọi người muốn sao chép một ví dụ ngẫu nhiên và sử dụng nó một cách mù quáng thì đó là vấn đề của họ.
Tro

Câu trả lời:


120

Điều đó rất hay, tuy nhiên để mô phỏng một thời gian trôi qua, chúng ta cần chạy một lệnh mất một thời gian và điều đó rất rõ ràng trong ví dụ thứ hai.

Tuy nhiên, phong cách sử dụng vòng lặp for để thực hiện một số chức năng mãi mãi tốn rất nhiều tài nguyên thiết bị và thay vào đó chúng ta có thể sử dụng Trình thu gom rác để làm một số việc như vậy.

Chúng ta có thể thấy sự sửa đổi này trong mã từ cùng một cuốn sách CLR Via C # Third Ed.

using System;
using System.Threading;

public static class Program {

   public static void Main() {
      // Create a Timer object that knows to call our TimerCallback
      // method once every 2000 milliseconds.
      Timer t = new Timer(TimerCallback, null, 0, 2000);
      // Wait for the user to hit <Enter>
      Console.ReadLine();
   }

   private static void TimerCallback(Object o) {
      // Display the date/time when this method got called.
      Console.WriteLine("In TimerCallback: " + DateTime.Now);
      // Force a garbage collection to occur for this demo.
      GC.Collect();
   }
}

3
Khalid, điều này là vô cùng hữu ích. Cảm ơn. Console.readline () và GC.Collect chính là thứ tôi cần.
Seth Spearman

9
@Ralph Willgoss, Tại sao GC.Collect (); bắt buộc?
Puchacz

2
@Puchacz Tôi không thấy một điểm gọi GC.Collect(). Không có gì để thu thập. Nó sẽ có ý nghĩa, nếu GC.KeepAlive(t)được gọi sauConsole.ReadLine();
newprint

1
Nó chấm dứt sau cuộc gọi lại đầu tiên
BadPiggie

1
@Khalid Al Hajami "Tuy nhiên, phong cách sử dụng vòng lặp for để thực hiện một số chức năng mãi mãi cần rất nhiều tài nguyên thiết bị và thay vào đó chúng ta có thể sử dụng Trình thu gom rác để làm một số việc như thế." Đây là rác vô nghĩa tuyệt đối. Công cụ thu gom rác hoàn toàn không liên quan. Bạn đã sao chép nó từ một cuốn sách và không hiểu những gì bạn đang sao chép?
Tro

65

Sử dụng lớp System.Threading.Timer.

System.Windows.Forms.Timer được thiết kế chủ yếu để sử dụng trong một luồng duy nhất thường là luồng UI của Windows Forms.

Ngoài ra còn có một lớp System.Timers được thêm vào sớm trong quá trình phát triển .NET framework. Tuy nhiên, thông thường nên sử dụng lớp System.Threading.Timer thay vì đây chỉ là một trình bao bọc xung quanh System.Threading.Timer.

Bạn cũng nên luôn luôn sử dụng Hệ thống tĩnh (được chia sẻ trong VB.NET) .Threading.Timer nếu bạn đang phát triển Dịch vụ Windows và yêu cầu bộ hẹn giờ để chạy định kỳ. Điều này sẽ tránh khả năng thu gom rác sớm của đối tượng hẹn giờ của bạn.

Đây là một ví dụ về bộ đếm thời gian trong ứng dụng giao diện điều khiển:

using System; 
using System.Threading; 
public static class Program 
{ 
    public static void Main() 
    { 
       Console.WriteLine("Main thread: starting a timer"); 
       Timer t = new Timer(ComputeBoundOp, 5, 0, 2000); 
       Console.WriteLine("Main thread: Doing other work here...");
       Thread.Sleep(10000); // Simulating other work (10 seconds)
       t.Dispose(); // Cancel the timer now
    }
    // This method's signature must match the TimerCallback delegate
    private static void ComputeBoundOp(Object state) 
    { 
       // This method is executed by a thread pool thread 
       Console.WriteLine("In ComputeBoundOp: state={0}", state); 
       Thread.Sleep(1000); // Simulates other work (1 second)
       // When this method returns, the thread goes back 
       // to the pool and waits for another task 
    }
}

Từ cuốn sách CLR Via C # của Jeff Richter. Nhân tiện, cuốn sách này mô tả cơ sở lý luận đằng sau 3 loại bộ định thời trong Chương 23, rất được khuyến khích.


Bạn có thể cung cấp thêm một chút thông tin về mã hóa thực tế?
Johan Bresler

Có ví dụ từ msdn làm việc cho bạn? msdn.microsoft.com/en-us/l
Library / system.threading.timer.aspx

Eric, tôi đã không thử nó nhưng sẽ không bất thường nếu có vấn đề với nó. Tôi nhận thấy nó cũng đang cố gắng thực hiện một số loại đồng bộ hóa giữa các luồng, đây là một khu vực có thể khó khăn để có được quyền. Nếu bạn có thể tránh nó trong thiết kế của bạn, nó luôn luôn thông minh để làm như vậy.
Tro

1
Ash - Tôi hoàn toàn đồng ý về các ví dụ msd. Tôi sẽ không ngay lập tức giảm giá mã đồng bộ hóa, nếu bộ hẹn giờ chạy trong luồng riêng của nó, thì bạn đang viết một ứng dụng đa luồng và cần lưu ý các vấn đề liên quan đến đồng bộ hóa.
Eric Tuttman

1
Điều gì xảy ra nếu có nhiều phương thức khớp với chữ ký đại biểu TimerCallback?
Ozkan

22

Đây là mã để tạo một dấu thời gian một giây đơn giản:

  using System;
  using System.Threading;

  class TimerExample
  {
      static public void Tick(Object stateInfo)
      {
          Console.WriteLine("Tick: {0}", DateTime.Now.ToString("h:mm:ss"));
      }

      static void Main()
      {
          TimerCallback callback = new TimerCallback(Tick);

          Console.WriteLine("Creating timer: {0}\n", 
                             DateTime.Now.ToString("h:mm:ss"));

          // create a one second timer tick
          Timer stateTimer = new Timer(callback, null, 0, 1000);

          // loop here forever
          for (; ; )
          {
              // add a sleep for 100 mSec to reduce CPU usage
              Thread.Sleep(100);
          }
      }
  }

Và đây là kết quả đầu ra:

    c:\temp>timer.exe
    Creating timer: 5:22:40

    Tick: 5:22:40
    Tick: 5:22:41
    Tick: 5:22:42
    Tick: 5:22:43
    Tick: 5:22:44
    Tick: 5:22:45
    Tick: 5:22:46
    Tick: 5:22:47

EDIT: Không bao giờ nên thêm các vòng lặp cứng vào mã khi chúng tiêu thụ chu kỳ CPU mà không thu được. Trong trường hợp này, vòng lặp đó đã được thêm vào chỉ để ngăn ứng dụng đóng lại, cho phép các hành động của luồng được quan sát. Nhưng vì mục đích chính xác và để giảm việc sử dụng CPU, một cuộc gọi Ngủ đơn giản đã được thêm vào vòng lặp đó.


7
For (;;) {} gây ra việc sử dụng cpu 100%.
Seth Spearman

1
Không phải là khá rõ ràng nếu bạn có một vòng lặp vô hạn thì điều đó sẽ dẫn đến CPU là 100%. Để khắc phục tất cả những gì bạn cần làm là thêm một cuộc gọi ngủ vào vòng lặp.
veight

3
Thật đáng ngạc nhiên khi có nhiều người được cố định về việc vòng lặp for có phải là vòng lặp while hay không và tại sao CPU lại đạt 100%. Nói về việc bỏ lỡ gỗ cho cây! Azimuth, cá nhân tôi muốn biết một thời gian (1) sẽ khác với vòng lặp vô hạn như thế nào? Chắc chắn những người viết trình tối ưu hóa trình biên dịch CLR sẽ đảm bảo hai cấu trúc mã này tạo ra cùng một mã CLR?
Blake7

1
Một lý do tại sao khi (1) sẽ không làm việc là nó không phải là hợp lệ c #: Test.cs (21,20): lỗi CS0031: giá trị hằng số '1' không thể được chuyển đổi sang một 'bool'
Blake7

1
Không phải trên máy của tôi (win8.1, i5), chỉ khoảng 20-30%, bạn đã có loại máy tính nào sau đó? @SethSpearman
shinzou

17

Hãy vui vẻ một chút

using System;
using System.Timers;

namespace TimerExample
{
    class Program
    {
        static Timer timer = new Timer(1000);
        static int i = 10;

        static void Main(string[] args)
        {            
            timer.Elapsed+=timer_Elapsed;
            timer.Start(); Console.Read();
        }

        private static void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            i--;

            Console.Clear();
            Console.WriteLine("=================================================");
            Console.WriteLine("                  DEFUSE THE BOMB");
            Console.WriteLine(""); 
            Console.WriteLine("                Time Remaining:  " + i.ToString());
            Console.WriteLine("");        
            Console.WriteLine("=================================================");

            if (i == 0) 
            {
                Console.Clear();
                Console.WriteLine("");
                Console.WriteLine("==============================================");
                Console.WriteLine("         B O O O O O M M M M M ! ! ! !");
                Console.WriteLine("");
                Console.WriteLine("               G A M E  O V E R");
                Console.WriteLine("==============================================");

                timer.Close();
                timer.Dispose();
            }

            GC.Collect();
        }
    }
}

11

Hoặc sử dụng Rx, ngắn và ngọt ngào:

static void Main()
{
Observable.Interval(TimeSpan.FromSeconds(10)).Subscribe(t => Console.WriteLine("I am called... {0}", t));

for (; ; ) { }
}

1
Giải pháp tốt nhất, thật đấy!
Dmitry Ledentsov

8
rất khó đọc và chống lại thực tiễn tốt nhất. Nó trông tuyệt vời nhưng không nên được sử dụng trong sản xuất vì một số ppl sẽ đi wtf và poo mình.
Piotr Kula

2
Phần mở rộng phản ứng (Rx) đã không được phát triển tích cực trong 2 năm. Ngoài ra các ví dụ là không có bối cảnh và khó hiểu. Ít để biết sơ đồ hoặc ví dụ dòng chảy.
James Bailey

4

Bạn cũng có thể sử dụng các cơ chế thời gian của riêng mình nếu bạn muốn kiểm soát nhiều hơn một chút, nhưng có thể kém chính xác hơn và nhiều mã / độ phức tạp hơn, nhưng tôi vẫn khuyên bạn nên sử dụng bộ hẹn giờ. Sử dụng điều này mặc dù nếu bạn cần kiểm soát luồng thời gian thực tế:

private void ThreadLoop(object callback)
{
    while(true)
    {
        ((Delegate) callback).DynamicInvoke(null);
        Thread.Sleep(5000);
    }
}

sẽ là chuỗi thời gian của bạn (sửa đổi điều này thành dừng khi được yêu cầu và tại bất kỳ khoảng thời gian nào bạn muốn).

và để sử dụng / bắt đầu, bạn có thể làm:

Thread t = new Thread(new ParameterizedThreadStart(ThreadLoop));

t.Start((Action)CallBack);

Gọi lại là phương thức không tham số void mà bạn muốn gọi ở mỗi khoảng thời gian. Ví dụ:

private void CallBack()
{
    //Do Something.
}

1
Nếu tôi muốn chạy một công việc hàng loạt cho đến khi hết thời gian, đề xuất của bạn ở đây có phải là công việc tốt nhất không?
Johan Bresler

1

Bạn cũng có thể tạo riêng của mình (nếu không hài lòng với các tùy chọn có sẵn).

Tạo Timerthực hiện của riêng bạn là công cụ khá cơ bản.

Đây là một ví dụ cho một ứng dụng cần truy cập đối tượng COM trên cùng một luồng với phần còn lại của cơ sở mã của tôi.

/// <summary>
/// Internal timer for window.setTimeout() and window.setInterval().
/// This is to ensure that async calls always run on the same thread.
/// </summary>
public class Timer : IDisposable {

    public void Tick()
    {
        if (Enabled && Environment.TickCount >= nextTick)
        {
            Callback.Invoke(this, null);
            nextTick = Environment.TickCount + Interval;
        }
    }

    private int nextTick = 0;

    public void Start()
    {
        this.Enabled = true;
        Interval = interval;
    }

    public void Stop()
    {
        this.Enabled = false;
    }

    public event EventHandler Callback;

    public bool Enabled = false;

    private int interval = 1000;

    public int Interval
    {
        get { return interval; }
        set { interval = value; nextTick = Environment.TickCount + interval; }
    }

    public void Dispose()
    {
        this.Callback = null;
        this.Stop();
    }

}

Bạn có thể thêm các sự kiện như sau:

Timer timer = new Timer();
timer.Callback += delegate
{
    if (once) { timer.Enabled = false; }
    Callback.execute(callbackId, args);
};
timer.Enabled = true;
timer.Interval = ms;
timer.Start();
Window.timers.Add(Environment.TickCount, timer);

Để đảm bảo bộ hẹn giờ hoạt động, bạn cần tạo một vòng lặp vô tận như sau:

while (true) {
     // Create a new list in case a new timer
     // is added/removed during a callback.
     foreach (Timer timer in new List<Timer>(timers.Values))
     {
         timer.Tick();
     }
}

1

Trong C # 5.0+ và .NET Framework 4.5+, bạn có thể sử dụng async / await:

async void RunMethodEvery(Action method, double seconds)
{
    while (true)
    {
        await Task.Delay(TimeSpan.FromSeconds(seconds));
        method();
    }
 }

0

tài liệu

Có bạn có nó :)

public static void Main()
   {
      SetTimer();

      Console.WriteLine("\nPress the Enter key to exit the application...\n");
      Console.WriteLine("The application started at {0:HH:mm:ss.fff}", DateTime.Now);
      Console.ReadLine();
      aTimer.Stop();
      aTimer.Dispose();

      Console.WriteLine("Terminating the application...");
   }

   private static void SetTimer()
   {
        // Create a timer with a two second interval.
        aTimer = new System.Timers.Timer(2000);
        // Hook up the Elapsed event for the timer. 
        aTimer.Elapsed += OnTimedEvent;
        aTimer.AutoReset = true;
        aTimer.Enabled = true;
    }

    private static void OnTimedEvent(Object source, ElapsedEventArgs e)
    {
        Console.WriteLine("The Elapsed event was raised at {0:HH:mm:ss.fff}",
                          e.SignalTime);
    }

0

Tôi khuyên bạn nên làm theo hướng dẫn của Microsoft ( https://docs.microsoft.com/en-us/dotnet/api/system.timers.timer.interval?view=netcore-3.1 ).

Lần đầu tiên tôi thử sử dụng System.Threading;với

var myTimer = new Timer((e) =>
{
   // Code
}, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));

nhưng nó liên tục dừng lại sau ~ 20 phút.

Với điều đó, tôi đã thử cài đặt giải pháp

GC.KeepAlive(myTimer)

hoặc là

for (; ; ) { }
}

nhưng họ đã không làm việc trong trường hợp của tôi.

Theo tài liệu của Microsoft, nó hoạt động hoàn hảo:

using System;
using System.Timers;

public class Example
{
    private static Timer aTimer;

    public static void Main()
    {
        // Create a timer and set a two second interval.
        aTimer = new System.Timers.Timer();
        aTimer.Interval = 2000;

        // Hook up the Elapsed event for the timer. 
        aTimer.Elapsed += OnTimedEvent;

        // Have the timer fire repeated events (true is the default)
        aTimer.AutoReset = true;

        // Start the timer
        aTimer.Enabled = true;

        Console.WriteLine("Press the Enter key to exit the program at any time... ");
        Console.ReadLine();
    }

    private static void OnTimedEvent(Object source, System.Timers.ElapsedEventArgs e)
    {
        Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
    }
}
// The example displays output like the following: 
//       Press the Enter key to exit the program at any time... 
//       The Elapsed event was raised at 5/20/2015 8:48:58 PM 
//       The Elapsed event was raised at 5/20/2015 8:49:00 PM 
//       The Elapsed event was raised at 5/20/2015 8:49:02 PM 
//       The Elapsed event was raised at 5/20/2015 8:49:04 PM 
//       The Elapsed event was raised at 5/20/2015 8:49:06 PM 
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.