오버랩을 방지하기 위해 타이머 동기화
-
22-08-2019 - |
문제
간격으로 가변 길이 활동을 실행하는 Windows 서비스를 작성하고 있습니다 (데이터베이스 스캔 및 업데이트). 이 작업이 자주 실행 되려면 처리 할 코드가 여러 번 동시에 실행되는 것이 안전하지 않습니다.
실행을 겹치지 않고 30 초마다 작업을 실행하기 위해 타이머를 가장 단순히 설정할 수있는 방법은 무엇입니까? (나는 가정하고있다 System.Threading.Timer
이 작업의 올바른 타이머이지만 착각 할 수 있습니다).
해결책
타이머로 수행 할 수 있지만 데이터베이스 스캔 및 업데이트에 어떤 형태의 잠금이 있어야합니다. 간단한 lock
동기화하기에 여러 실행이 발생하지 않도록 충분할 수 있습니다.
즉, 작동이 완료된 후 타이머를 시작하고 한 번만 사용한 다음 중지하는 것이 좋습니다. 다음 작업 후에 다시 시작하십시오. 이것은 이벤트들 사이에 30 초 (또는 n 초)를, 겹칠 가능성이없고 잠금이 없을 것입니다.
예시 :
System.Threading.Timer timer = null;
timer = new System.Threading.Timer((g) =>
{
Console.WriteLine(1); //do whatever
timer.Change(5000, Timeout.Infinite);
}, null, 0, Timeout.Infinite);
즉시 일을 ..... 마무리 ... 기다려 5
다른 팁
나는 당신의 경과 코드에서 Monitor.ryenter를 사용합니다.
if (Monitor.TryEnter(lockobj))
{
try
{
// we got the lock, do your work
}
finally
{
Monitor.Exit(lockobj);
}
}
else
{
// another elapsed has the lock
}
나는 선호한다 System.Threading.Timer
이벤트 처리 메커니즘을 거치지 않아도되기 때문에 이와 같은 것들.
Timer UpdateTimer = new Timer(UpdateCallback, null, 30000, 30000);
object updateLock = new object();
void UpdateCallback(object state)
{
if (Monitor.TryEnter(updateLock))
{
try
{
// do stuff here
}
finally
{
Monitor.Exit(updateLock);
}
}
else
{
// previous timer tick took too long.
// so do nothing this time through.
}
}
타이머를 원샷으로 만들고 모든 업데이트 후에 다시 시작하여 잠금의 필요성을 제거 할 수 있습니다.
// Initialize timer as a one-shot
Timer UpdateTimer = new Timer(UpdateCallback, null, 30000, Timeout.Infinite);
void UpdateCallback(object state)
{
// do stuff here
// re-enable the timer
UpdateTimer.Change(30000, Timeout.Infinite);
}
잠금 대신 (모든 시간이 지정된 스캔을 기다리고 결국 쌓을 수 있음). 스레드에서 스캔/업데이트를 시작한 다음 스레드가 아직 살아 있는지 확인하기 만하면됩니다.
Thread updateDBThread = new Thread(MyUpdateMethod);
...
private void timer_Elapsed(object sender, ElapsedEventArgs e)
{
if(!updateDBThread.IsAlive)
updateDBThread.Start();
}
Autoresetevent를 다음과 같이 사용할 수 있습니다.
// Somewhere else in the code
using System;
using System.Threading;
// In the class or whever appropriate
static AutoResetEvent autoEvent = new AutoResetEvent(false);
void MyWorkerThread()
{
while(1)
{
// Wait for work method to signal.
if(autoEvent.WaitOne(30000, false))
{
// Signalled time to quit
return;
}
else
{
// grab a lock
// do the work
// Whatever...
}
}
}
약간 "더 똑똑한"솔루션은 의사 코드에서 다음과 같습니다.
using System;
using System.Diagnostics;
using System.Threading;
// In the class or whever appropriate
static AutoResetEvent autoEvent = new AutoResetEvent(false);
void MyWorkerThread()
{
Stopwatch stopWatch = new Stopwatch();
TimeSpan Second30 = new TimeSpan(0,0,30);
TimeSpan SecondsZero = new TimeSpan(0);
TimeSpan waitTime = Second30 - SecondsZero;
TimeSpan interval;
while(1)
{
// Wait for work method to signal.
if(autoEvent.WaitOne(waitTime, false))
{
// Signalled time to quit
return;
}
else
{
stopWatch.Start();
// grab a lock
// do the work
// Whatever...
stopwatch.stop();
interval = stopwatch.Elapsed;
if (interval < Seconds30)
{
waitTime = Seconds30 - interval;
}
else
{
waitTime = SecondsZero;
}
}
}
}
이 중 하나는 이벤트를 신호를 보내면 스레드를 종료 할 수 있다는 이점이 있습니다.
편집하다
이 코드는 당신 이이 myworkerthreads () 중 하나만 실행한다고 가정합니다. 그렇지 않으면 동시에 실행됩니다.
단일 실행을 원할 때 뮤트를 사용했습니다.
private void OnMsgTimer(object sender, ElapsedEventArgs args)
{
// mutex creates a single instance in this application
bool wasMutexCreatedNew = false;
using(Mutex onlyOne = new Mutex(true, GetMutexName(), out wasMutexCreatedNew))
{
if (wasMutexCreatedNew)
{
try
{
//<your code here>
}
finally
{
onlyOne.ReleaseMutex();
}
}
}
}
너무 늦어서 죄송합니다 ... getMutexName () 메소드 호출의 일부로 뮤트 이름을 제공해야합니다.