2012-01-30 8 views
11

나는 List<System.Threading.Timer>이 있습니다. 각 타이머는 구성 가능한 간격 (기본값 10 분)으로 실행됩니다. 모두 동일한 콜백 메소드를 호출합니다 (다른 매개 변수 사용). 콜백 메소드는 작업을 완료하는 데 몇 초가 걸릴 수 있습니다.프로그램 종료 전에 System.Threading.Timer 콜백 대기 완료

프로그램이 종료되면 콜백 메소드의 실행이 즉시 중단 된 것처럼 보입니다 (올바르게 표시됩니까?).

프로그램을 종료하기 전에 현재 실행중인 콜백 메소드가 완료 될 때까지 기다리는 방법은 무엇입니까?

답변

16

당신은 WaitHandler 매개 변수를 사용하여 모든 타이머를 폐기 할 수 있습니다. 콜백 메소드가 완료되면이 핸들러는 신호됩니다 (사양 말한대로 ". 타이머는 모든 현재 대기중인 콜백이 완료 될 때까지 배치되지 않는다")

void WaitUntilCompleted(List<Timer> myTimers) 
{ 
    List<WaitHandle> waitHnd = new List<WaitHandle>(); 
    foreach (var timer in myTimers) 
    { 
     WaitHandle h = new AutoResetEvent(false); 
     if(!timer.Dispose(h)) throw new Exception("Timer already disposed."); 
     waitHnd.Add(h); 
    } 
    WaitHandle.WaitAll(waitHnd.ToArray()); 
} 

편집 : @ 피터는 폐기 방법의 중요성을 강조 반환 값. 타이머가 이미 처리되면 false를 반환합니다. 이 솔루션이 안정적으로 유지 될 수 있도록하기 위해 타이머를 이미 처리했을 때 콜백이 끝났을 때 제어 할 수 없으므로 예외를 던지도록 수정했습니다. 이전 콜백이 여전히 실행 중일 수 있음에도 불구하고!

+0

방금 ​​테스트 한 결과와 @Peter가 맞습니다. 여러 번 처리하면 교착 상태가 발생할 수 있습니다. 그의 대답은 정확한 것입니다. –

+0

@Peter 솔루션에 동의하지 않으면 내 편집을 읽어보십시오. – Tomek

-2

콘솔 응용 프로그램에서 종료 될 때까지 기다릴 수 없습니다. 당신은 콜백 시작과 종료에 감소 될 때마다 증가됩니다 정적 실행 콜백 카운터 변수를 만들 수 있습니다

Windows 용

는 응용 프로그램을 형성한다. 물론 이렇게 할 때 잠금을 사용해야합니다.

그런 다음 해당 이벤트를 확인하고 카운터가 0이 될 때까지 기다리든지 아니면 그냥 종료를 취소 할 수 있는지 확인할 수 있습니다.

+0

실제로, 그것은 ;-) http://msdn.microsoft.com/en-gb/library/system.console.cancelkeypress(v=vs입니다. 80) .aspx – Seb

+0

Tomek의 솔루션은 Console App에서 잘 작동합니다. 귀하의 솔루션은 콘솔 앱에서도 작동합니다.메인이 종료되기 전에 카운터가 0에 도달하기를 기다리는 카운터를 폴링해야합니다. –

1

보류중인 작업이 완료 될 때까지 ManualResetEvents을 사용하여 주 스레드를 차단할 수 있습니다. 당신은 당신이 설정 초기 상태를 가진 System.Threading.ManualResetEvent[] 배열을 가질 수 모든 타이머가 적어도 한 번 한 후 실행하려는 경우 예를 들어

비 신호 코드에 따라서 어딘가에

을 당신은 당신의 타이머 설정을 할 것이다 그리고 그것은 연관되어 있습니다.

// in main setup method.. 
int frequencyInMs = 600000; //10 mins 
Timer timer = new Timer(); 
timer.Elapsed += (s, e) => MyExecute(); 
myTimers.Add(timer) 

ManualResetEvent[] _waithandles = new ManualResetEvent[10]; 
_waithandles[0] = new ManualResetEvent(false); 

// Other timers ... 
timer = new Timer(); 
timer.Elapsed += (s, e) => MyOtherExecute(); 
myTimers.Add(timer)   
_waithandles[1] = new ManualResetEvent(false); 
// etc, and so on for all timers 

// then in each method that gets executed by the timer 
// simply set ManualReset event to signalled that will unblock it. 
private void MyExecute() 
{ 
    // do all my logic then when done signal the manual reset event 
    _waithandles[0].Set(); 
} 

// In your main before exiting, this will cause the main thread to wait 
// until all ManualResetEvents are set to signalled 
WaitHandle.WaitAll(_waithandles);  

당신은 단순히 이런 일에 수정 완료 보류중인 작업을 대기하고 싶었다면 :

_waithandles[0] = new ManualResetEvent(true); // initial state set to non blocking. 

private void MyExecute() 
{ 
    _waithandles[0].Reset(); // set this waithandle to block.. 

    // do all my logic then when done signal the manual reset event 
    _waithandles[0].Set(); 
} 
3

Tomek의 대답은 훌륭하지만 불완전합니다. Dispose 함수가 false를 반환하면 스레드가 이미 완료되었으므로 완료를 기다릴 필요가 없음을 의미합니다. 이 경우 WaitHandle을 기다리려고하면 WaitAll이 반환되지 않으므로 응용 프로그램/스레드를 임의로 고정시키는 함수를 직접 만들었습니다. 여기

는 보는 방법이다 :

void WaitUntilCompleted(List<Timer> myTimers) 
    { 
     List<WaitHandle> waitHnd = new List<WaitHandle>(); 
     foreach (var timer in myTimers) 
     { 
      WaitHandle h = new AutoResetEvent(false); 
      if (timer.Dispose(h)) 
      { 
       waitHnd.Add(h); 
      } 
     } 
     WaitHandle.WaitAll(waitHnd.ToArray()); 
    } 
+1

Disrupt는 Timer에서 두 번 이상 호출 된 경우에만 false를 반환합니다. Timer의 콜백이 완료 되어도 Dispose 반환 값에는 영향을주지 않습니다. – Tomek

+0

콜백이 단발 타이머 인 경우 콜백이 아직 시작되지 않았는지 확인해야합니다. 이렇게하면 영원히 대기하게됩니다. –