2010-08-06 4 views
13

db의 특정 테이블을 60 초마다 확인하는 Windows 서비스를 만들었습니다. 추가 된 모든 새로운 행에 대해 서버에서 60 초 이상 걸릴 수있는 무거운 프로세싱을 수행해야합니다.이전 스레드가 여전히 바쁠 경우 Timer가 틱을 건너 뛰게하는 방법

저는 서비스에서 Timer 객체를 만들었습니다. Timer 객체는 매 60 초마다 틱하고 원하는 메서드를 호출합니다.
발견 된 새 행을 처리하는 동안이 타이머를 틱하지 않으려면 lock { } 블록의 메서드를 래핑 했으므로 다른 스레드가 액세스 할 수 없습니다. 내가 궁금하네요 지금

Timer serviceTimer = new Timer(); 
serviceTimer.Interval = 60; 
serviceTimer.Elapsed += new ElapsedEventHandler(serviceTimer_Elapsed); 
serviceTimer.Start(); 

void serviceTimer_Elapsed(object sender, ElapsedEventArgs e) 
{ 
    lock (this) 
    { 
     // do some heavy processing... 
    } 
} 

- 내 타이머 틱 경우
을하고, DB에 새로운 행을 많이 발견, 지금은 처리가 이상 걸릴 것

그것은 다음과 같이 보입니다 60 초가 지나면 이전 틱이 끝날 때까지 다음 틱은 아무런 처리도하지 않습니다. 이것이 내가 원하는 효과이다.

하지만 이제 첫 번째 처리가 완료되면 serviceTimer_Elapsed 메소드가 즉시 종료되거나 타이머가 다시 똑딱 거리기를 기다릴 것입니다.

내가 원하는 것은 - 처리가 60 초 이상 필요한 경우 타이머가 스레드가 잠겨 있음을 감지하고 다시 확인하기 위해 60 초 기다렸으므로 다시는 확인하지 않아도됩니다. 이전의 thread가 종료 할 때까지 대기하고있는 thread의 큐

어떻게이 결과를 얻을 수 있습니까?
이렇게하는 것이 가장 좋은 방법은 무엇입니까?

감사합니다.

답변

19

당신은

// Just in case someone wants to inherit your class and lock it as well ... 
private static object _padlock = new object(); 
try 
{ 
    serviceTimer.Stop(); 

    lock (_padlock) 
    { 
     // do some heavy processing... 
    } 
} 
finally 
{ 
    serviceTimer.Start(); 
} 

편집처럼 처리하는 동안 타이머, 뭔가를 해제 시도 할 수 있습니다 : OP는 재진입 만 타이머 또는 서비스가 멀티 스레드인지를 발생되었는지 여부를 지정하지 않았습니다. 나중에 가정했으나 타이머가 중지 된 경우 (자동 재설정 또는 수동으로) 이전 잠금이 필요하지 않은 경우

+11

사용하지 마십시오'잠금 (이)'-에 http : // stackoverflow.com/questions/251391/why-is-lockthis-bad –

+3

autoreset을 false로 설정하는 것이 더 쉬울 것입니다. 그런 다음 아무 것도 잠글 필요가 없습니다. – javapowered

7

서비스가 실행 중인지 빠르게 확인하십시오. 실행중인 경우이 이벤트를 건너 뛰고 다음 이벤트가 발생할 때까지 기다립니다.

Timer serviceTimer = new Timer(); 
serviceTimer.Interval = 60; 
serviceTimer.Elapsed += new ElapsedEventHandler(serviceTimer_Elapsed); 
serviceTimer.Start(); 
bool isRunning = false; 
void serviceTimer_Elapsed(object sender, ElapsedEventArgs e) 
{ 
    lock (this) 
    { 
     if(isRunning) 
      return; 
     isRunning = true; 
    } 
    try 
    { 
    // do some heavy processing... 
    } 
    finally 
    { 
     isRunning = false; 
    } 
} 
+1

나는 nonnb의 솔루션을 광산보다 더 좋아하지만 내 것을 떠날 것입니다. 행사를 중지 할 수없는 경우 사례를 제공하십시오. –

+0

또한 이벤트가 계속 실행되도록 허용하는 것이 일부 컨텍스트에서는 유용합니다. 메인 프로세스 외부에서 기록하거나 다른 처리를 수행 할 수 있습니다. –

2

다른 옵션으로는 BackGroundWorker 클래스 또는 TheadPool.QueueUserWorkItem을 사용할 수 있습니다.

배경 작업자는 현재 진행중인 현재 처리 옵션을 한 번에 쉽게 확인하고 처리 할 수 ​​있습니다. ThreadPool은 필요한 경우 틱 (tick)마다 항목을 대기열에 계속 대기시킬 수있는 기능을 제공합니다.

설명에서 데이터베이스의 대기열에있는 항목을 확인한다고 가정합니다. 이 경우 ThreadPool을 사용하여 작업을 백그라운드로 푸시하고 느린 속도로/검사 메커니즘을 중지하지 마십시오.

서비스의 경우 ThreadPool 접근 방식을 사용하는 것이 좋습니다. 이렇게하면 타이머로 60 초마다 새 항목을 확인한 다음 대기열에 넣고 .Net에서 각 항목에 얼마만큼 할당 할 지 파악하고 항목을 대기열에 계속 푸시 할 수 있습니다.

예 : 타이머를 사용하고 5 개의 새로운 행이있는 경우 65 초의 처리 시간이 필요합니다. ThreadPool 방식을 사용하면 5 초의 배경 작업 항목으로 65 초 만에 완료됩니다. Timer 방식을 사용하면 4 분 이상 (각 행 사이에 대기하는 시간)이 소요되며 대기열에있는 다른 작업의 백 로그가 발생할 수 있습니다.

Timer serviceTimer = new Timer(); 
    void startTimer() 
    { 
     serviceTimer.Interval = 60; 
     serviceTimer.Elapsed += new ElapsedEventHandler(serviceTimer_Elapsed); 
     serviceTimer.AutoReset = false; 
     serviceTimer.Start(); 
    } 
    void serviceTimer_Elapsed(object sender, ElapsedEventArgs e) 
    { 
     try 
     { 
      // Get your rows of queued work requests 

      // Now Push Each Row to Background Thread Processing 
      foreach (Row aRow in RowsOfRequests) 
      { 
       ThreadPool.QueueUserWorkItem(
        new WaitCallback(longWorkingCode), 
        aRow); 
      } 
     } 
     finally 
     { 
      // Wait Another 60 Seconds and check again 
      serviceTimer.Stop(); 
     } 
    } 

    void longWorkingCode(object workObject) 
    { 
     Row workRow = workObject as Row; 
     if (workRow == null) 
      return; 

     // Do your Long work here on workRow 
    } 
19

당신은이 경우 잠금이 필요하지 않습니다 : 여기에

이 완료되어야하는 방법의 예입니다. 타이머를 시작하기 전에 timer.AutoReset = false로 설정하십시오. 처리가 끝나면 처리기에서 타이머를 다시 시작하십시오. 이렇게하면 각 작업이 이후에 타이머가 60 초 번으로 실행됩니다.

+2

+1 Autoreset이라는 속성이 있다는 것을 결코 알지 못했습니다. – Searock

0

에 관심이있을 수있는 전체 대답 : 다른 답변에

void serviceTimer_Elapsed(object sender, ElapsedEventArgs e) 
{ 
    if (System.Threading.Monitor.IsLocked(yourLockingObject)) 
     return; 
    else 
     lock (yourLockingObject) 
     // your logic 
      ; 
} 
5

유사한 변화, 타이머가 똑딱 유지할 수와 작업 할 때 할 만 타이머를 멈추지 않고 잠금을 얻을 수 있습니다.

이 경과 이벤트 처리기에 넣고 :

if (Monitor.TryEnter(locker) 
{ 
    try 
    { 
     // Do your work here. 
    } 
    finally 
    { 
     Monitor.Exit(locker); 
    } 
} 
+0

이것은 제가 사용하는 접근 방법입니다. 경과 이벤트 핸들러가 넘어지고 타이머가 홀로 남을 수 있습니다. –

2

반응성 확장과 함께이 문제를 해결 꽤 깔끔한 방법이있다. 여기 코드는, 당신은 여기 풀러 설명을 읽을 수 있습니다 : http://www.zerobugbuild.com/?p=259

public static IDisposable ScheduleRecurringAction(
    this IScheduler scheduler, 
    TimeSpan interval, 
    Action action) 
{ 
    return scheduler.Schedule(
     interval, scheduleNext => 
    { 
     action(); 
     scheduleNext(interval); 
    }); 
} 

그리고이처럼 사용할 수 있습니다

TimeSpan interval = TimeSpan.FromSeconds(5); 
Action work =() => Console.WriteLine("Doing some work..."); 

var schedule = Scheduler.Default.ScheduleRecurringAction(interval, work);   

Console.WriteLine("Press return to stop."); 
Console.ReadLine(); 
schedule.Dispose(); 
+2

이것은 꽤 멋진 해결책이며 아마도 즉시 명백하지 않은 RX 스케줄러의 흥미로운 사용법을 보여줍니다. – jamesmus

+0

이 솔루션은 저에게 효과적입니다. 저에게 스케줄러에 대한 새로운 통찰력을 주셨습니다. – jumpercake

관련 문제