C # 콘솔 응용 프로그램에 타이머를 추가하는 방법


132

그냥-C # 콘솔 응용 프로그램에 타이머를 어떻게 추가합니까? 예제 코딩을 제공 할 수 있다면 좋을 것입니다.


16
주의 : 여기에 대답에는 버그가 있으며 Timer 객체는 가비지 수집됩니다. 타이머에 대한 참조는 계속 똑딱 거리는 정적 변수에 저장해야합니다.
Hans Passant

@HansPassant 당신은 내 대답에서 명확한 진술을 놓친 것 같습니다 : "Windows 서비스를 개발하고 주기적으로 타이머를 실행 해야하는 경우 항상 정적 (VB.NET에서 공유) System.Threading.Timer를 사용하는 것이 좋습니다 이렇게하면 타이머 개체의 조기 가비지 수집을 피할 수 있습니다. " 사람들이 임의의 예를 복사하고 그것을 맹목적으로 사용하려면 그것이 문제입니다.
Ash

답변:


120

시간이 지남에 따라 시뮬레이션하려면 시간이 걸리는 명령을 실행해야하며 두 번째 예에서는 매우 분명합니다.

그러나 for 루프를 사용하여 일부 기능을 수행하는 스타일에는 많은 장치 리소스가 필요하지만 가비지 수집기를 사용하여 이와 같은 작업을 수행 할 수 있습니다.

동일한 책 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
칼리드, 이것은 매우 도움이되었다. 감사. console.readline ()과 GC.Collect는 내가 필요한 것입니다.
세스 스피어 맨

9
@Ralph Willgoss, 왜 GC.Collect (); 필요합니까?
Puchacz

2
@Puchacz 전화의 요점을 볼 수 없습니다 GC.Collect(). 수집 할 것이 없습니다. GC.KeepAlive(t)호출 된 경우 다음과 같이 의미 가 있습니다.Console.ReadLine();
newprint

1
첫 콜백 후 종료 됨
BadPiggie

1
@Khalid Al Hajami "그러나 일부 기능을 수행하기 위해 for 루프를 사용하는 스타일에는 많은 장치 리소스가 필요하지만 가비지 수집기를 사용하여 이와 같은 작업을 수행 할 수 있습니다." 이것은 무의미한 쓰레기입니다. 가비지 수집기는 전혀 관련이 없습니다. 책에서이 내용을 복사했으며 복사 한 내용을 이해하지 못했습니까?
Ash

65

System.Threading.Timer 클래스를 사용하십시오.

System.Windows.Forms.Timer는 주로 단일 스레드, 일반적으로 Windows Forms UI 스레드에서 사용하도록 설계되었습니다.

.NET 프레임 워크 개발 초기에 System.Timers 클래스가 추가되었습니다. 그러나 System.Threading.Timer 클래스는 System.Threading.Timer 주위의 래퍼이므로 일반적으로 대신 System.Threading.Timer 클래스를 사용하는 것이 좋습니다.

Windows 서비스를 개발 중이고 주기적으로 타이머를 실행해야하는 경우 항상 정적 (VB.NET에서 공유) System.Threading.Timer를 사용하는 것이 좋습니다. 이것은 타이머 객체의 조기 가비지 수집을 피할 것입니다.

다음은 콘솔 응용 프로그램의 타이머 예입니다.

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의 CLR Via C # 책에서 . 그런데이 책은 23 장의 3 가지 유형의 타이머에 대한 이론적 근거를 적극 권장합니다.


실제 코딩에 대한 정보를 조금 더 제공 할 수 있습니까?
Johan Bresler

msdn의 예가 당신을 위해 작동합니까? msdn.microsoft.com/ko-kr/library/system.threading.timer.aspx
Eric Tuttleman

에릭, 나는 그것을 시도하지 않았지만 문제가 있다면 드문 일이 아닙니다. 스레드 간 동기화를 시도하고 있음을 알았습니다. 이것은 항상 올바르게 이해하기 어려운 영역입니다. 디자인에서 피할 수 있다면 항상 현명합니다.
Ash

1
애쉬-나는 분명히 msdn 예제에 동의합니다. timmer가 자체 스레드에서 실행되는 경우 동기화 코드를 즉시 할인하지는 않지만 다중 스레드 응용 프로그램을 작성 중이며 동기화와 관련된 문제를 알아야합니다.
Eric Tuttleman

1
TimerCallback 델리게이트 서명과 일치하는 여러 메소드가있는 경우 어떻게됩니까?
Ozkan

22

간단한 1 초 타이머 틱을 만드는 코드는 다음과 같습니다.

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

결과는 다음과 같습니다.

    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

편집 : 코드에 하드 스핀 루프를 추가하지 않고 CPU 사이클을 소비하므로 코드에 추가하는 것은 좋은 생각이 아닙니다. 이 경우 응용 프로그램이 닫히지 않도록 루프가 추가되어 스레드의 동작을 관찰 할 수 있습니다. 그러나 정확성을 위해 그리고 CPU 사용량을 줄이기 위해 간단한 Sleep 호출이 해당 루프에 추가되었습니다.


7
for (;;) {}로 인해 CPU 사용률이 100 %가됩니다.
세스 스피어 맨

1
무한 for 루프가 있으면 CPU가 100 %가되는 것이 분명하지 않습니다. 이를 해결하려면 루프에 휴면 호출을 추가하면됩니다.
veight

3
for 루프가 while 루프 여야하는지 여부와 CPU가 100 %가되는 이유에 얼마나 많은 사람들이 고정되어 있는지는 놀랍습니다. 나무의 나무를 놓치다에 대해 이야기하십시오! 방위각, 나는 개인적으로 while (1)이 무한 for 루프와 어떻게 다른지 알고 싶습니다. CLR 컴파일러 옵티 마이저를 작성하는 사람들은이 두 코드 구성이 정확히 동일한 CLR 코드를 생성하게 할 것입니까?
Blake7

1
while (1)이 작동하지 않는 한 가지 이유는 c # : test.cs (21,20)가 유효하지 않기 때문입니다. 오류 CS0031 : 상수 값 '1'은 (는) 'bool'로 변환 할 수 없습니다.
Blake7

1
내 컴퓨터 (win8.1, i5)가 아닌 약 20-30 %, 그때 어떤 종류의 컴퓨터를 사용 했습니까? @SethSpearman
shinzou

17

조금 재미있게 보내자

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

또는 짧고 달콤한 Rx를 사용하십시오.

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

for (; ; ) { }
}

1
정말 최고의 솔루션입니다!
Dmitry Ledentsov

8
읽을 수 없으며 모범 사례에 위배됩니다. 그것은 훌륭해 보이지만 일부 ppl은 wtf와 poo 자체를 가지므로 프로덕션에는 사용해서는 안됩니다.
Piotr Kula

2
Rx (Reactive Extensions)는 2 년 동안 적극적으로 개발되지 않았습니다. 또한 예제에는 상황과 혼동이 없습니다. 다이어그램이나 플로우 예제는 거의 알 수 없습니다.
James Bailey

4

약간의 제어를 원하지만 정확도와 코드 / 복잡성을 낮추려면 자체 타이밍 메커니즘을 사용할 수도 있지만 타이머를 권장합니다. 실제 타이밍 스레드를 제어해야하는 경우이를 사용하십시오.

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

타이밍 스레드가 될 것입니다 (요청 할 때, 원하는 시간 간격으로 중지하도록 수정하십시오).

사용 / 시작하려면 다음을 수행하십시오.

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

t.Start((Action)CallBack);

콜백은 매 간격마다 호출하려는 void 매개 변수가없는 메서드입니다. 예를 들면 다음과 같습니다.

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

1
시간이 초과 될 때까지 배치 작업을 실행하려면 여기에 당신의 제안이 가장 좋습니까?
Johan Bresler

1

사용 가능한 옵션에 만족하지 않는 경우 직접 만들 수도 있습니다.

자신의 Timer구현을 만드는 것은 매우 기본적인 것입니다.

이것은 내 코드베이스의 나머지 부분과 동일한 스레드에서 COM 개체 액세스가 필요한 응용 프로그램의 예입니다.

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

}

다음과 같이 이벤트를 추가 할 수 있습니다.

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

타이머가 작동하도록하려면 다음과 같이 무한 루프를 만들어야합니다.

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 이상 및 .NET Framework 4.5 이상에서는 async / await를 사용할 수 있습니다.

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

0

문서

거기 있습니다 :)

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 지침 ( https://docs.microsoft.com/en-us/dotnet/api/system.timers.timer.interval?view=netcore-3.1) 을 따르는 것이 좋습니다. ) .

나는 처음에 System.Threading;함께 사용해 보았습니다.

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

~ 20 분 후에 계속 중지되었습니다.

그걸로 솔루션 설정을 시도했습니다.

GC.KeepAlive(myTimer)

또는

for (; ; ) { }
}

그러나 그들은 내 경우에는 효과가 없었습니다.

Microsoft 설명서에 따르면 완벽하게 작동했습니다.

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 
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.