C # konsol uygulamasına nasıl zamanlayıcı eklersiniz


132

Sadece bu - Bir C # konsol uygulamasına nasıl zamanlayıcı eklersiniz? Örnek bir kodlama sunabilirseniz harika olur.


16
Dikkat: Buradaki cevaplarda bir hata var, Timer nesnesi çöpleri toplayacak. Zamanlayıcıya referans, sürekli çalıştığından emin olmak için statik bir değişkende saklanmalıdır.
Hans Passant

@HansPassant Cevabımdaki net ifadeyi kaçırmış gibisiniz: "Ayrıca bir Windows Hizmeti geliştiriyorsanız ve periyodik olarak çalışmak için bir zamanlayıcıya ihtiyaç duyuyorsanız, her zaman statik (VB.NET'te paylaşılan) bir Sistem kullanmanız önerilir. . Bu, zamanlayıcı nesnenizin olası erken çöp toplamasını önleyecektir. " İnsanlar rastgele bir örneği kopyalayıp körü körüne kullanmak istiyorlarsa bu onların sorunu.
Ash

Yanıtlar:


120

Bu çok güzel, ancak geçen zamanı simüle etmek için biraz zaman alan bir komut çalıştırmamız gerekiyor ve bu ikinci örnekte çok açık.

Bununla birlikte, bazı işlevleri sonsuza kadar yapmak için bir for döngüsü kullanma stili çok fazla cihaz kaynağı gerektirir ve bunun yerine bunun gibi bir şey yapmak için Çöp Toplayıcıyı kullanabiliriz.

Bu değişikliği aynı CLR Via C # Third Ed kitabındaki kodda görebiliriz.

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, bu çok yardımcı oldu. Teşekkürler. Console.readline () ve GC.Collect tam da ihtiyacım olan şeydi.
Seth Spearman

9
@Ralph Willgoss, Neden GC.Collect (); gerekli?
Puchacz

2
@Puchacz Bir arama noktası görmüyorum GC.Collect(). Toplanacak hiçbir şey yok. Daha GC.KeepAlive(t)sonra Console.ReadLine();
çağrılsaydı

1
İlk geri
aramadan

1
@Khalid Al Hajami "Bununla birlikte, bazı işlevleri sonsuza kadar yapmak için for döngüsü kullanma tarzı çok fazla cihaz kaynağı gerektirir ve bunun yerine bunun gibi bir şey yapmak için Çöp Toplayıcıyı kullanabiliriz." Bu tamamen saçma bir saçmalık. Çöp toplayıcı tamamen alakasız. Bunu bir kitaptan kopyaladınız ve ne kopyaladığınızı anlamadınız mı?
Ash

66

System.Threading.Timer sınıfını kullanın.

System.Windows.Forms.Timer, öncelikle tek bir iş parçacığında, genellikle Windows Forms UI iş parçacığında kullanılmak üzere tasarlanmıştır.

Ayrıca .NET çerçevesinin geliştirilmesinin başlarında eklenen bir System.Timers sınıfı da vardır. Bununla birlikte, genellikle System.Threading.Timer sınıfının kullanılması önerilir çünkü bu, yine de System.Threading.Timer etrafında bir sarmalayıcıdır.

Ayrıca, bir Windows Hizmeti geliştiriyorsanız ve periyodik olarak çalışmak için bir zamanlayıcı gerekiyorsa, her zaman statik (VB.NET'te paylaşılan) bir System.Threading.Timer kullanmanız önerilir. Bu, zamanlayıcı nesnenizin olası erken çöp toplamasını önleyecektir.

Aşağıda, bir konsol uygulamasındaki bir zamanlayıcı örneği verilmiştir:

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

Jeff Richter'in CLR Via C # kitabından . Bu arada, bu kitap Bölüm 23'teki 3 tür zamanlayıcının arkasındaki mantığı açıklamaktadır, şiddetle tavsiye edilmektedir.


Gerçek kodlama hakkında biraz daha bilgi verebilir misiniz?
Johan Bresler

Msdn'deki örnek sizin için çalışıyor mu? msdn.microsoft.com/en-us/library/system.threading.timer.aspx
Eric Tuttleman

Eric, denemedim ama bir sorun olsaydı alışılmadık olmazdı. Ayrıca bir tür iş parçacığı arası senkronizasyon yapmaya çalıştığını fark ettim, bu her zaman doğru yapmak için zor olabilecek bir alandır. Tasarımınızda bundan kaçınabiliyorsanız, bunu yapmak her zaman akıllıca olacaktır.
Ash

1
Ash - msdn örneklerine kesinlikle katılıyorum. Zamanlayıcı kendi iş parçacığında çalışıyorsa, senkronizasyon kodunu hemen indirmeyeceğim, o zaman çok iş parçacıklı bir uygulama yazıyorsunuz ve senkronizasyonla ilgili sorunların farkında olmanız gerekiyor.
Eric Tuttleman

1
TimerCallback temsilci imzasıyla eşleşen birden çok yöntem varsa ne olur?
Özkan

22

İşte bir saniyelik basit bir zamanlayıcı tik oluşturmak için kod:

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

Ve işte ortaya çıkan çıktı:

    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

DÜZENLEME: Kazanım olmadan CPU döngülerini tükettiklerinden koda sert döndürme döngüleri eklemek asla iyi bir fikir değildir. Bu durumda, döngü sadece uygulamanın kapanmasını durdurmak için eklendi ve iş parçacığının eylemlerinin gözlemlenmesine izin verildi. Ancak doğruluk ve CPU kullanımını azaltmak için bu döngüye basit bir Uyku çağrısı eklendi.


7
For (;;) {}% 100 cpu kullanımına neden olur.
Seth Spearman

1
Sonsuz bir for döngüsüne sahipseniz, bu% 100'lük bir CPU ile sonuçlanacak oldukça açık değil mi? Bunu düzeltmek için yapmanız gereken tek şey döngüye bir uyku çağrısı eklemektir.
veight

3
Kaç kişinin for döngüsünün bir while döngüsü olması gerektiğine ve CPU'nun neden% 100'e gittiğine karar vermesi şaşırtıcı. Ağaçlar için odun özleminden bahsedin! Azimuth, ben şahsen bir süre (1) sonsuz for döngüsünden ne kadar farklı olacağını bilmek isterim? Şüphesiz, CLR derleyici iyileştiricisini yazan kişiler, bu iki kod yapısının tam olarak aynı CLR kodunu oluşturduğundan emin olacaktır?
Blake7

1
While (1) çalışmamasının bir nedeni, geçerli olmamasıdır c #: test.cs (21,20): hata CS0031: Sabit değer '1' bir '
bool'a

1
Benim makinemde değil (win8.1, i5), sadece% 20-30 civarında, o zamanlar ne tür bir bilgisayarınız vardı? @SethSpearman
shinzou

17

Biraz Eğlenelim

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

Veya Rx kullanarak, kısa ve tatlı:

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

for (; ; ) { }
}

1
en iyi çözüm, gerçekten!
Dmitry Ledentsov

8
çok okunamaz ve en iyi uygulamaya aykırı. Harika görünüyor, ancak üretimde kullanılmamalıdır çünkü bazı ppl'ler wtf'ye gidecek ve kaka yapacaktır.
Piotr Kula

2
Reaktif Uzantılar (Rx) 2 yıldır aktif olarak geliştirilmemiştir. Ek olarak, örnekler bağlamsız ve kafa karıştırıcıdır. Şemaları veya akış örneklerini bilmek çok az.
James Bailey

4

Biraz daha fazla kontrol, ancak muhtemelen daha az doğruluk ve daha fazla kod / karmaşıklık istiyorsanız, kendi zamanlama mekanizmalarınızı da kullanabilirsiniz, ancak yine de bir zamanlayıcı tavsiye ederim. Gerçek zamanlama iş parçacığı üzerinde kontrol sahibi olmanız gerekiyorsa bunu kullanın:

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

zamanlama iş parçacığınız olabilir (bunu, gerektiğinde ve istediğiniz zaman aralığında duracak şekilde değiştirin).

ve kullanmak / başlatmak için şunları yapabilirsiniz:

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

t.Start((Action)CallBack);

Geri arama, her aralıkta çağrılmasını istediğiniz void parametresiz yönteminizdir. Örneğin:

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

1
Bir toplu işi zaman aşımına uğrayana kadar çalıştırmak istersem, buradaki öneriniz en iyisi olur mu?
Johan Bresler

1

Ayrıca kendinizinkini de oluşturabilirsiniz (mevcut seçeneklerden memnun değilseniz).

Kendi Timeruygulamanızı oluşturmak oldukça basit şeylerdir.

Bu, kod tabanımın geri kalanıyla aynı iş parçacığı üzerinde COM nesne erişimine ihtiyaç duyan bir uygulama örneğidir.

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

}

Olayları aşağıdaki şekilde ekleyebilirsiniz:

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

Zamanlayıcının çalıştığından emin olmak için aşağıdaki gibi sonsuz bir döngü oluşturmanız gerekir:

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

C # 5.0+ ve .NET Framework 4.5+ sürümlerinde zaman uyumsuz / beklemeyi kullanabilirsiniz:

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

0

doktor

İşte aldı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

Microsoft yönergelerini ( https://docs.microsoft.com/en-us/dotnet/api/system.timers.timer.interval?view=netcore-3.1 ) izlemenizi öneririm .

İlk kullanarak çalıştı System.Threading;ile

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

ancak ~ 20 dakika sonra sürekli olarak durdu.

Bununla çözüm ayarını denedim

GC.KeepAlive(myTimer)

veya

for (; ; ) { }
}

ama benim durumumda işe yaramadı.

Microsoft belgelerini takiben mükemmel çalıştı:

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 
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.