2011-02-02 5 views
10

표준 System.Timers.Timer 동작에 문제가 있습니다. 타이머는 일정 간격으로 Elapsed 이벤트를 발생시킵니다. 그러나 Elapsed 이벤트 핸들러 내에서 실행 시간이 타이머 간격을 초과하면 스레드 풀이 이벤트 처리 대기열에 들어가기 시작합니다. 이건 내 문제 야. 이는 Elapsed 이벤트 핸들러를 사용하여 데이터베이스에서 데이터를 가져 와서 데이터를 가져 와서 결과를 다시 데이터베이스에 저장하기 때문입니다. 그러나 데이터 처리는 한 번만 제공되어야합니다. 그래서, System.Timers.Timer에 대한 경과 이벤트를 큐에 저장하지 못하게하는 방법이 있습니까?System.Timers.Timer가 스레드 풀에서 실행 대기열에 들어가는 것을 방지하려면 어떻게해야합니까?

public class EntryPoint 
{ 

    private static void TimeProc(object state, ElapsedEventArgs e) 
    { 
     Console.WriteLine("Current time {0} on the thread {1}", DateTime.Now, Thread.CurrentThread.ManagedThreadId); 
     Thread.Sleep(20000); 
    } 

    static void Main(string[] args) 
    { 
     Console.WriteLine("Press <Enter> for finishing\n\n"); 
     ThreadPool.SetMaxThreads(10, 10); 
     System.Timers.Timer MyTimer = new System.Timers.Timer(1000); 
     MyTimer.Elapsed += new ElapsedEventHandler(TimeProc); 
     MyTimer.Start(); 
     Console.ReadLine(); 
     MyTimer.Stop(); 
    } 
} 

가능한 출력으로 여기에있을 것입니다 :

Current time 03.02.2011 0:00:09 on the thread 4 
Current time 03.02.2011 0:00:10 on the thread 5 
Current time 03.02.2011 0:00:12 on the thread 6 
Current time 03.02.2011 0:00:13 on the thread 7 
Current time 03.02.2011 0:00:14 on the thread 8 
Current time 03.02.2011 0:00:15 on the thread 9 
Current time 03.02.2011 0:00:16 on the thread 10 
Current time 03.02.2011 0:00:17 on the thread 11 
Current time 03.02.2011 0:00:18 on the thread 12 
Current time 03.02.2011 0:00:19 on the thread 13 
Current time 03.02.2011 0:00:30 on the thread 4 
Current time 03.02.2011 0:00:30 on the thread 5 

가능한 솔루션 : 당신이 다음 테스트 프로그램 고려할 수있는이 문제에 대한 그림으로

1) 그것은 에서 영감을 얻었습니다. C# Timer vs Thread in Service

그리고 여기 샘플 위에서 언급에 대한 같은 코드가 있습니다

public class EntryPoint 
    { 
     private static System.Timers.Timer MyTimer; 
     private static void TimeProc(object state, ElapsedEventArgs e) 
     { 
      Console.WriteLine("Current time {0} on the thread {1}", DateTime.Now, Thread.CurrentThread.ManagedThreadId); 
      Thread.Sleep(20000); 
      MyTimer.Enabled = true; 
     } 

     static void Main(string[] args) 
     { 
      Console.WriteLine("Press <Enter> for finishing\n\n"); 
      ThreadPool.SetMaxThreads(10, 10); 
      MyTimer = new System.Timers.Timer(1000); 
      MyTimer.AutoReset = false; 

      MyTimer.Elapsed += new ElapsedEventHandler(TimeProc); 
      MyTimer.Enabled = true; 
      Console.ReadLine(); 

     } 
    } 

2) 두 번째 방법은 SynchronizingObject의에 관한 것입니다,하지만 그것은 단지 윈도우 폼 응용 프로그램 또는 그 것 오브젝트를 구현하기위한 코드의 필요한 추가 개발을위한 가치 ISynchronizeInvoke 인터페이스를 구현해야합니다. 이 방법에 대해 자세히 알아보십시오 here

그래서 지금은 우선 해결책을 선호합니다.

+0

:

여기
private bool setTimerBodyRunning() { bool retVal = false; lock (timerBodyRunning) { //timerBodyRunning is type object and it holds a bool. //The reason it is object and not bool is so it can be locked on to ensure thread safety if (!((bool)timerBodyRunning)) { timerBodyRunning = true; retVal = true; } } return retVal; } private void setTimerBodyFinished() { lock (timerBodyRunning) { timerBodyRunning = false; } } 

당신이 타이머를 초기화하고 시작 했죠 방법 . Elapsed 이벤트를 대기열에 올려 놓는 것이 좋습니다. 하드웨어 인터럽트라면 인터벌을 날려 버릴 수 있습니다. –

+0

데이터를 한 번만 처리해야하는 이유가 반복 타이머 인 이유는 무엇입니까? 아니면 X 시간마다 한 번만을 의미합니까? – Chris

+0

@Chris 내 경우에는 다른 응용 프로그램이 데이터베이스에 일부 데이터를 저장하고 응용 프로그램은이를 읽고 처리해야합니다. – apros

답변

14

이 경우 일반적으로 수행하는 작업은 Elapsed 처리기의 시작 부분에서 타이머를 중지하고 마지막에 다시 시작하는 것입니다. 이렇게하면 한 번에 한 진드기 만 처리 할 수 ​​있습니다.

업데이트 : MSDN 링크 당

, 나는 그들이 무엇을 의미하는 것은 당신이 당신의 자신의 플래그를 설정 (여전히 틱이 들어오고있다)하지만, 스레드 안전 대책뿐만 아니라주의해야한다 수 있다는 것입니다 생각합니다.

+0

네, 맞습니다. 그러나 그것은 타이머를 멈추고 시작하기위한 추가 자원이 필요했기 때문에 바른 방법이 아닙니다. – apros

+1

@apros : 발생하는 동작은 의도적으로 설계된 동작입니다. 타이머를 매 30 초마다 틱 단위로 설정하면 두 번의 틱 이벤트가 발생합니다. 다르게 행동하기를 원하면이 방법을 사용하십시오. –

+0

MSDN claim here : http://msdn.microsoft.com/en-us/library/system.timers.timer.elapsed.aspx "이 경쟁 조건을 해결하는 한 가지 방법은 이벤트 처리기에 후속 이벤트를 무시하기 위해 경과 된 이벤트. " 따라서이 문제를 해결할 다른 방법이 있어야합니다. – apros

4

나는 이것을 단순히 멈추고 다음과 같이 길게 실행 한 후에 시작한다고 말하고 싶습니다.

tmr.Stop(); 
//Your lengthy execution code goes here 
tmr.Start(); 
+1

그것은 일반적으로 내가하는 일이지만 stop을 사용할 때 이것을 알고 있어야합니다. http://msdn.microsoft.com/en-us/library/system.timers.timer.stop.aspx 노란색으로 시작 부분 멈추는 동안 두 번째 점심에서 사용자에게 경고합니다. – lollancf37

+0

@ lollancf37 : 이점은 무엇입니까? – Chris

+0

@Chris 만약 당신이 스레드에 대해 이야기하지 않았다면 그의 경우에는 그렇지만, 그리 멀지 않은 곳에서 나는 내 servie에있는 tomer와 함께 일했고 나는 나의 실행 코드를 끝내면서 다른 이벤트를 포착 할 수 있어야만했다. 그것이 내가 그것에 대해 이야기하기 시작한 이유입니다. – lollancf37

4

표시되는 동작은 의도적으로 설계된 동작입니다. 타이머에 SynchronizingObject을 설정하거나 다중 스레드에서 틱하지 않는 다른 타이머 (예 : System.Threading.Timer)를 사용하십시오.

+0

System.Threading.Timer와 관련된 정보가 잘못되었습니다. http://msdn.microsoft.com/en-us/library/system.threading.timer.aspx - "타이머가 실행하는 콜백 메소드는 ThreadPool 스레드에서 호출되기 때문에 재진입되어야합니다. 콜백을 실행할 수 있습니다 타이머 간격이 콜백을 실행하는 데 필요한 시간보다 작은 경우 또는 동시에 모든 스레드 풀 스레드가 사용되고 콜백이 여러 번 대기중인 경우 두 개의 스레드 풀 스레드에서 동시에 발생합니다. " 아마도 System.Windows.Forms.Timer – bkr

+0

을 생각해 보았을 것입니다. (이해가 정확하다면) 동기화 객체를 설정하면 대기열을 막지 못하게 될 것입니다. 단 한 번에 하나씩 처리하도록 제한 할 것입니다. 열. 동작에 대한 세부 사항을 다루는 타이머 비교는 http://msdn.microsoft.com/en-us/magazine/cc164015.aspx를 참조하십시오. – bkr

2

방금 ​​정적 플래그 변수를 만듭니다. 이 방법은 내 타이머가 계속 실행되지만 다음 타이머주기 전에 메소드가 완료되지 않은 경우 코드가 무시됩니다.

타이머에 사용되는 방법에서 이벤트가 진행 중인지 테스트합니다.

Timer_Method_Called() 
{ 
    if (eventInProgress == 0) 
    { 
    // flag event as in progress 
    eventInProcess == 1; 

    // perform code.... 

    // after code is complete, allow the method to execute 
    eventInProgress == 0; 
    } 
} 
+4

이것은 스레드로부터 안전하지 않으며, ==를 사용하여 당신이 당신의 임무 (=)라고 가정하고있는 것에 대해 사용하고 있습니다. – bkr

2

답변 중 어느 것도 스레드로부터 안전하지 않기 때문에, 내가 하나 제안하자 : 당신이 볼 수 있듯이 아직 실행하지 않은 경우

void oneHundredMS_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { 

    if (setTimerBodyRunning()) { //only proceed to body if it is not already processing; setTimerBodyRunning must be thread-safe 
    // here you do your long running operation 
    setTimerBodyFinished(); 
    } 
} 

, 타이머 핸들러를 먼저 확인보고를하고, false가 반환 된 경우에만 본문으로 이동합니다.true가 반환되면 핸들러는 빠르게 반환되고 틱은 대기열에 있지 않습니다 (단순 잠금 문이 사용되었을 수 있음). 여기 setTimerBodyRunning에 대한 정의는 다음과 setTimerBodyFinished이 도처에 발생하는 타이머가 문제가 ...하지 않습니다

object timerBodyRunning = new object(); 
timerBodyRunning = false; 
System.Timers.Timer timerFrequency100MS = new System.Timers.Timer(); 
timerFrequency100MS.Interval = FREQUENCY_MS; //it will fire every 100 milliseconds 
timerFrequency100MS.Elapsed += new System.Timers.ElapsedEventHandler(oneHundredMS_Elapsed); 
timerFrequency100MS.Start(); 
+0

이것은 스레드 안전 문제를 처리합니다. finally 블록에 setTimerBodyFinished 호출을 두어 항상 호출되는지 확인하십시오. – user3112728

관련 문제