C #의 System.Threading.Timer가 작동하지 않는 것 같습니다. 3 초마다 매우 빠르게 실행됩니다.


112

타이머 개체가 있습니다. 매분 실행되기를 바랍니다. 특히, OnCallBack메서드를 실행해야 하며 OnCallBack메서드가 실행되는 동안 비활성화됩니다 . OnCallBack메서드가 완료 되면 (a OnCallBack) 타이머를 다시 시작합니다.

지금 내가 가지고있는 것은 다음과 같습니다.

private static Timer timer;

private static void Main()
{
    timer = new Timer(_ => OnCallBack(), null, 0, 1000 * 10); //every 10 seconds
    Console.ReadLine();
}

private static void OnCallBack()
{
    timer.Change(Timeout.Infinite, Timeout.Infinite); //stops the timer
    Thread.Sleep(3000); //doing some long operation
    timer.Change(0, 1000 * 10);  //restarts the timer
}

그러나 작동하지 않는 것 같습니다. 3 초마다 매우 빠르게 실행됩니다. 기간을 올리더라도 (1000 * 10). 눈이 멀어 보이는 것 같습니다.1000 * 10

내가 뭘 잘못 했어?


12
From Timer.Change: "dueTime이 0이면 콜백 메소드가 즉시 호출됩니다." 나에게는 제로인 것 같습니다.
Damien_The_Unbeliever

2
예, 근데 뭐요? 기간도 있습니다.
Alan Coromano

10
마침표도 있으면 어떨까요? 인용 된 문장은 기간 값에 대해 어떠한 주장도하지 않습니다. "이 값이 0이면 즉시 콜백을 호출하겠습니다"라고 표시됩니다.
Damien_The_Unbeliever

3
흥미롭게도 dueTime과 period를 모두 0으로 설정하면 타이머가 매초마다 실행되고 즉시 시작됩니다.
Kelvin

답변:


230

이것은 System.Threading.Timer의 올바른 사용법이 아닙니다. Timer를 인스턴스화 할 때 거의 항상 다음을 수행해야합니다.

_timer = new Timer( Callback, null, TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite );

이렇게하면 간격이 경과했을 때 타이머가 한 번만 틱하도록 지시합니다. 그런 다음 콜백 기능에서 작업이 완료되면 타이머를 변경하십시오. 예:

private void Callback( Object state )
{
    // Long running operation
   _timer.Change( TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite );
}

따라서 동시성이 없기 때문에 잠금 메커니즘이 필요하지 않습니다. 타이머는 다음 간격과 장기 실행 작업 시간이 경과 한 후 다음 콜백을 시작합니다.

타이머를 정확히 N 밀리 초로 실행해야하는 경우 스톱워치를 사용하여 장기 실행 작업 시간을 측정 한 다음 적절하게 Change 메서드를 호출하는 것이 좋습니다.

private void Callback( Object state )
{
   Stopwatch watch = new Stopwatch();

   watch.Start();
   // Long running operation

   _timer.Change( Math.Max( 0, TIME_INTERVAL_IN_MILLISECONDS - watch.ElapsedMilliseconds ), Timeout.Infinite );
}

나는 강력하게 - 사람이 .NET을 수행하고, 제프리 리히터의 책을 읽을 수있다 CLR 사용하고 격려 C #을 통해 CLR을 가능한 한 빨리를 읽기. 타이머와 스레드 풀은 여기에 매우 자세히 설명되어 있습니다.


6
나는 그것에 동의하지 않는다 private void Callback( Object state ) { // Long running operation _timer.Change( TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite ); }. Callback작업이 완료되기 전에 다시 호출 될 수 있습니다.
Alan Coromano

2
내가 의미 한 것은 그 Long running operation때 훨씬 더 많은 시간이 걸릴 수 있다는 것 TIME_INTERVAL_IN_MILLISECONDS입니다. 그러면 무슨 일이 일어날까요?
Alan Coromano 2012 년

31
콜백이 다시 호출되지 않는 것이 포인트입니다. 이것이 Timeout.Infinite를 두 번째 매개 변수로 전달하는 이유입니다. 이것은 기본적으로 타이머에 대해 다시 틱하지 않음을 의미합니다. 그런 다음 작업을 완료 한 후 틱하도록 일정을 변경하십시오.
Ivan Zlatanov

여기 스레딩 초보자- ThreadPool타이머를 전달하면으로 할 수 있다고 생각 하십니까? 특정 간격으로 작업을 수행하기 위해 새 스레드가 생성 된 다음 완료되면 스레드 풀로 강등되는 시나리오를 생각하고 있습니다.
jedd.ahyoung

2
System.Threading.Timer는 전용 스레드가 아닌 스레드 풀에서 콜백을 실행하는 스레드 풀 타이머입니다. 타이머가 콜백 루틴을 완료하면 콜백을 실행 한 스레드가 풀로 돌아갑니다.
Ivan Zlatanov 2014 년

14

타이머를 중지 할 필요는 없습니다 . 이 게시물에서 좋은 솔루션을 참조하십시오 .

"타이머가 콜백 메서드를 계속 실행하도록 할 수 있지만 Monitor.TryEnter / Exit에 재진입이 아닌 코드를 래핑 할 수 있습니다.이 경우 타이머를 중지 / 다시 시작할 필요가 없습니다. 중복 호출은 잠금을 획득하지 않고 즉시 반환됩니다."

private void CreatorLoop(object state) 
 {
   if (Monitor.TryEnter(lockObject))
   {
     try
     {
       // Work here
     }
     finally
     {
       Monitor.Exit(lockObject);
     }
   }
 }

제 경우가 아닙니다. 타이머를 정확히 중지해야합니다.
Alan Coromano

콜백 입력을 한 번 이상 방지하려고합니까? 달성하려는 것이 아니라면?
이반 Leonenko

1. 콜백에 한 번 이상 들어 가지 않도록합니다. 2. 너무 많은 시간 실행을 방지합니다.
Alan Coromano

이것이 정확히하는 일입니다. # 2는 객체가 잠긴 경우 if 문 바로 뒤에 반환되는 한, 특히 그렇게 큰 간격이있는 경우에는 많은 오버 헤드가 아닙니다.
Ivan Leonenko 2012 년

1
이것은 코드가 마지막 실행 후 <interval> 이상으로 호출된다는 것을 보장하지 않습니다 (타이머의 새로운 틱은 이전 틱이 잠금을 해제 한 후 마이크로 초가 될 수 있음). 이것이 엄격한 요구 사항인지 아닌지에 따라 다릅니다 (문제 설명에서 완전히 명확하지 않음).
마르코 헌병

9

사용은 System.Threading.Timer필수인가요?

그렇지 않은 경우 System.Timers.Timer편리한 Start()Stop()메서드 (및 AutoResetfalse로 설정할 수 있는 속성이 있으므로 Stop()필요하지 않고 Start()실행 후 호출 하기 만하면됩니다 ).


3
예,하지만 실제 요구 사항 일 수도 있고, 가장 많이 사용되는 타이머이기 때문에 방금 선택했을 수도 있습니다. 슬프게도 .NET에는 90 %에 걸쳐 겹치는 수많은 타이머 개체가 있지만 여전히 (때로는 미묘하게) 다릅니다. 물론 요구 사항이라면이 솔루션은 전혀 적용되지 않습니다.
Marco Mp

2
당으로 문서를 다음 Systems.Timer 클래스는 닷넷 프레임 워크에서 사용할 수 있습니다. .NET 표준 라이브러리에 포함되어 있지 않으며 .NET Core 또는 유니버설 Windows 플랫폼
NotAgain 말한다 Reinstate Monica

3

나는 그냥 할 것입니다.

private static Timer timer;
 private static void Main()
 {
   timer = new Timer(_ => OnCallBack(), null, 1000 * 10,Timeout.Infinite); //in 10 seconds
   Console.ReadLine();
 }

  private static void OnCallBack()
  {
    timer.Dispose();
    Thread.Sleep(3000); //doing some long operation
    timer = new Timer(_ => OnCallBack(), null, 1000 * 10,Timeout.Infinite); //in 10 seconds
  }

주기를 직접 제어하려고하므로주기 매개 변수를 무시하십시오.


당신이 지정 유지하기 때문에 원래 코드는 가능한 한 빨리 실행 0에 대한 dueTime매개 변수입니다. 에서 Timer.Change:

dueTime이 영 (0)이면 콜백 메서드가 즉시 호출됩니다.


2
타이머 폐기가 필요합니까? 왜 Change()방법 을 사용하지 않습니까?
Alan Coromano

21
매번 타이머를 폐기하는 것은 절대적으로 불필요하고 잘못되었습니다.
Ivan Zlatanov

0
 var span = TimeSpan.FromMinutes(2);
 var t = Task.Factory.StartNew(async delegate / () =>
   {
        this.SomeAsync();
        await Task.Delay(span, source.Token);
  }, source.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);

source.Cancel(true/or not);

// or use ThreadPool(whit defaul options thread) like this
Task.Start(()=>{...}), source.Token)

내부에 루프 스레드를 사용하는 것을 좋아한다면 ...

public async void RunForestRun(CancellationToken token)
{
  var t = await Task.Factory.StartNew(async delegate
   {
       while (true)
       {
           await Task.Delay(TimeSpan.FromSeconds(1), token)
                 .ContinueWith(task => { Console.WriteLine("End delay"); });
           this.PrintConsole(1);
        }
    }, token) // drop thread options to default values;
}

// And somewhere there
source.Cancel();
//or
token.ThrowIfCancellationRequested(); // try/ catch block requred.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.