문제

간격으로 가변 길이 활동을 실행하는 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 () 메소드 호출의 일부로 뮤트 이름을 제공해야합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top