Bagaimana Anda menambahkan timer ke aplikasi konsol C #

132

Hanya ini - Bagaimana Anda menambahkan timer ke aplikasi konsol C #? Akan lebih bagus jika Anda bisa menyediakan beberapa contoh pengkodean.

Johan Bresler
sumber
16
Perhatian: jawaban di sini memiliki bug, objek Timer akan mengumpulkan sampah. Referensi ke timer harus disimpan dalam variabel statis untuk memastikannya terus berdetak.
Hans Passant
@HansPassant Anda sepertinya telah melewatkan pernyataan yang jelas dalam jawaban saya: "Juga disarankan untuk selalu menggunakan sistem statis (dibagi dalam VB.NET). Memulai. Jika Anda sedang mengembangkan Layanan Windows dan memerlukan timer untuk berjalan secara berkala Ini akan menghindari kemungkinan pengumpulan sampah prematur dari objek pengatur waktu Anda. " Jika orang ingin menyalin contoh acak dan menggunakannya secara membabi buta itu masalah mereka.
Ash

Jawaban:

120

Itu sangat bagus, namun untuk mensimulasikan beberapa waktu yang berlalu kita perlu menjalankan perintah yang membutuhkan waktu dan itu sangat jelas dalam contoh kedua.

Namun, gaya menggunakan loop untuk melakukan beberapa fungsi selamanya membutuhkan banyak sumber daya perangkat dan sebagai gantinya kita dapat menggunakan Pengumpul Sampah untuk melakukan hal seperti itu.

Kita dapat melihat modifikasi ini dalam kode dari buku yang sama 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();
   }
}
Khalid Al Hajami
sumber
3
Khalid, ini sangat membantu. Terima kasih. The console.readline () dan GC.Collect adalah apa yang saya butuhkan.
Seth Spearman
9
@Ralph Willgoss, Why GC.Collect (); diperlukan?
Puchacz
2
@ Puchacz Saya tidak melihat titik menelepon GC.Collect(). Tidak ada yang dikumpulkan. Akan masuk akal, jika GC.KeepAlive(t)dipanggil setelahConsole.ReadLine();
cetak baru
1
Itu dihentikan setelah panggilan balik pertama
BadPiggie
1
@Khalid Al Hajami "Namun, gaya menggunakan loop for untuk melakukan fungsionalitas selamanya membutuhkan banyak sumber daya perangkat dan sebagai gantinya kita dapat menggunakan Pengumpul Sampah untuk melakukan hal-hal seperti itu." Ini benar-benar sampah yang tidak masuk akal. Pengumpul sampah sama sekali tidak relevan. Apakah Anda menyalin ini dari buku dan tidak mengerti apa yang Anda salin?
Ash
65

Gunakan kelas System.Threading.Timer.

System.Windows.Forms.Timer dirancang terutama untuk digunakan dalam utas tunggal biasanya utas Windows Forms UI.

Ada juga kelas System.Timers ditambahkan sejak awal dalam pengembangan kerangka NET. Namun umumnya disarankan untuk menggunakan kelas System.Threading.Timer sebagai gantinya ini hanya pembungkus di sekitar System.Threading.Timer saja.

Dianjurkan juga untuk selalu menggunakan Sistem statis (dibagi dalam VB.NET) System.Threading.Timer jika Anda mengembangkan Layanan Windows dan memerlukan timer untuk berjalan secara berkala. Ini akan menghindari kemungkinan pengumpulan sampah prematur dari objek pengatur waktu Anda.

Berikut ini contoh penghitung waktu di aplikasi konsol:

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

Dari buku CLR Via C # oleh Jeff Richter. Ngomong-ngomong buku ini menggambarkan alasan di balik 3 jenis penghitung waktu di Bab 23, sangat dianjurkan.

Abu
sumber
Bisakah Anda memberikan sedikit informasi lebih lanjut tentang pengkodean aktual?
Johan Bresler
Apakah contoh dari msdn bekerja untuk Anda? msdn.microsoft.com/en-us/library/system.threading.timer.aspx
Eric Tuttleman
Eric, saya belum mencobanya tetapi tidak aneh jika ada masalah dengan itu. Saya perhatikan itu juga mencoba melakukan semacam sinkronisasi antar-thread, ini selalu merupakan area yang sulit untuk dilakukan dengan benar. Jika Anda dapat menghindarinya dalam desain Anda, itu selalu pintar untuk melakukannya.
Ash
1
Ash - Saya pasti setuju tentang contoh msdn. Saya tidak akan segera mengabaikan kode sinkronisasi, jika timer berjalan di utasnya sendiri, maka Anda menulis aplikasi multi-utas dan perlu mengetahui masalah yang berkaitan dengan sinkronisasi.
Eric Tuttleman
1
Apa yang terjadi jika ada beberapa metode yang cocok dengan tanda tangan delegasi TimerCallback?
Ozkan
22

Berikut adalah kode untuk membuat centang satu detik sederhana:

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

Dan inilah output yang dihasilkan:

    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: Tidak pernah merupakan ide yang baik untuk menambahkan hard spin loop ke dalam kode karena mereka mengkonsumsi siklus CPU tanpa hasil. Dalam hal ini, loop ditambahkan hanya untuk menghentikan aplikasi agar tidak menutup, sehingga tindakan dari thread dapat diamati. Tetapi demi kebenaran dan untuk mengurangi penggunaan CPU panggilan Sleep sederhana telah ditambahkan ke loop itu.

jussij
sumber
7
For (;;) {} menyebabkan 100% penggunaan CPU.
Seth Spearman
1
Bukankah sudah cukup jelas jika Anda memiliki infinite untuk loop maka itu akan menghasilkan CPU 100%. Untuk memperbaikinya yang perlu Anda lakukan adalah menambahkan panggilan tidur ke loop.
veight
3
Sungguh menakjubkan berapa banyak orang yang terpaku pada apakah for loop harus sementara dan mengapa CPU mencapai 100%. Bicara tentang merindukan kayu untuk pohon! Azimuth, saya pribadi ingin tahu bagaimana beberapa saat (1) akan berbeda dengan infinite untuk loop? Tentunya orang-orang yang menulis optimizer kompiler CLR akan memastikan dua konstruksi kode ini membuat kode CLR yang sama persis?
Blake7
1
Salah satu alasan mengapa sementara (1) tidak berfungsi adalah tidak valid c #: test.cs (21,20): error CS0031: Nilai konstan '1' tidak dapat dikonversi ke 'bool'
Blake7
1
Tidak di komputer saya (win8.1, i5), hanya sekitar 20-30%, komputer seperti apa yang Anda miliki saat itu? @SethSpearman
shinzou
17

Mari Bersenang-senang

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();
        }
    }
}
Caz nyata
sumber
11

Atau menggunakan Rx, pendek dan manis:

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

for (; ; ) { }
}
Yonatan Zetuny
sumber
1
solusi terbaik, sungguh!
Dmitry Ledentsov
8
sangat tidak dapat dibaca dan bertentangan dengan praktik terbaik. Itu terlihat luar biasa tetapi tidak boleh digunakan dalam produksi karena beberapa ppl akan pergi wtf dan buang air besar sendiri.
Piotr Kula
2
Ekstensi Reaktif (Rx) belum aktif dikembangkan selama 2 tahun. Selain itu contoh-contohnya tanpa konteks dan membingungkan. Sedikit tahu diagram atau contoh mengalir.
James Bailey
4

Anda juga dapat menggunakan mekanisme pengaturan waktu Anda sendiri jika Anda ingin sedikit lebih banyak kontrol, tetapi mungkin kurang akurat dan lebih banyak kode / kompleksitas, tetapi saya masih akan merekomendasikan timer. Gunakan ini meskipun jika Anda harus memiliki kontrol atas utas waktu aktual:

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

akan menjadi utas waktu Anda (modifikasi ini untuk berhenti ketika diperlukan, dan pada interval waktu apa pun yang Anda inginkan).

dan untuk menggunakan / memulai Anda dapat melakukan:

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

t.Start((Action)CallBack);

Callback adalah metode tanpa parameter kosong yang ingin Anda panggil pada setiap interval. Sebagai contoh:

private void CallBack()
{
    //Do Something.
}
mattlant
sumber
1
Jika saya ingin menjalankan pekerjaan batch sampai habis, akankah saran Anda di sini menjadi yang terbaik?
Johan Bresler
1

Anda juga dapat membuat sendiri (jika tidak puas dengan opsi yang tersedia).

Membuat Timerimplementasi Anda sendiri adalah hal yang cukup mendasar.

Ini adalah contoh untuk aplikasi yang membutuhkan akses objek COM pada utas yang sama dengan sisa basis kode saya.

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

}

Anda dapat menambahkan acara sebagai berikut:

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

Untuk memastikan timer berfungsi, Anda perlu membuat loop tanpa akhir sebagai berikut:

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();
     }
}
Steven de Salas
sumber
1

Dalam C # 5.0+ dan .NET Framework 4.5+ Anda dapat menggunakan async / menunggu:

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

dokter

Itu dia :)

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);
    }
XvXLuka222
sumber
0

Saya sarankan Anda mengikuti pedoman Microsoft ( https://docs.microsoft.com/en-us/dotnet/api/system.timers.timer.interval?view=netcore-3.1 ).

Saya pertama kali mencoba menggunakan System.Threading;dengan

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

tetapi terus berhenti setelah ~ 20 menit.

Dengan itu, saya mencoba pengaturan solusi

GC.KeepAlive(myTimer)

atau

for (; ; ) { }
}

tetapi mereka tidak bekerja dalam kasus saya.

Berikut dokumentasi Microsoft, ini bekerja dengan sempurna:

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 
Alessio Di Salvo
sumber