C # 타이머가 별도의 스레드에서 경과합니까?


98

System.Timers.Timer가 그것을 생성 한 스레드가 아닌 별도의 스레드에서 경과합니까?

5 초마다 실행되는 타이머가있는 수업이 있다고 가정 해 보겠습니다. 타이머가 실행되면 elapsed 메서드에서 일부 개체가 수정됩니다. 이 개체를 수정하는 데 10 초처럼 오랜 시간이 걸린다고 가정 해 보겠습니다. 이 시나리오에서 스레드 충돌이 발생할 수 있습니까?


이로 인해 문제가 발생할 수 있습니다. 일반적으로 스레드 풀 스레드는 장기 실행 프로세스 용으로 설계되지 않았습니다.
Greg D

나는 Windows 서비스로 테스트하면서 이것을 실행하고있었습니다. 나를 위해 일한 것은 OnTimer 이벤트의 첫 번째 명령으로 타이머를 비활성화하고 내 작업을 수행 한 다음 마지막에 타이머를 활성화하는 것입니다. 이것은 한동안 프로덕션 환경에서 안정적으로 작동했습니다.
Steve

답변:


60

대한 System.Timers.Timer :

아래 Brian Gideon의 답변을 참조하십시오.

대한 이 System.Threading.Timer :

타이머에 대한 MSDN 설명서는 다음과 같이 설명합니다.

System.Threading.Timer 클래스 는 ThreadPool 스레드에서 콜백을 만들고 이벤트 모델을 전혀 사용하지 않습니다.

따라서 실제로 타이머는 다른 스레드에서 경과합니다.


21
사실이지만 완전히 다른 클래스입니다. OP는 System.Timers.Timer 클래스에 대해 물었습니다.
Brian Gideon

1
오, 맞아요. msdn.microsoft.com/en-us/library/system.timers.timer.aspx에 "ThreadPool 스레드에서 Elapsed 이벤트가 발생했습니다."라고 표시됩니다. 나는 거기에서 같은 결론을 내린다.
Joren

6
네,하지만 그렇게 간단하지는 않습니다. 내 대답을 참조하십시오.
Brian Gideon

192

때에 따라 다르지. 는 System.Timers.Timer두 가지 작동 모드가 있습니다.

SynchronizingObjectISynchronizeInvoke인스턴스로 설정된 경우 Elapsed동기화 개체를 호스팅하는 스레드 에서 이벤트가 실행됩니다. 보통 이러한 ISynchronizeInvoke경우는 보통 오래된 이외의 아무것도 없습니다 Control그리고 Form우리는 모든 잘 알고있는 것으로 인스턴스. 따라서이 경우 Elapsed이벤트는 UI 스레드에서 호출되며 System.Windows.Forms.Timer. 그렇지 않으면 실제로 ISynchronizeInvoke사용 된 특정 인스턴스 에 따라 다릅니다 .

경우 SynchronizingObjectIS 그때는 null Elapsed이벤트가 불려 ThreadPool스레드 그리고 그것은 유사 동작 System.Threading.Timer. 사실, 실제로는 System.Threading.Timer이면에서를 사용하고 필요한 경우 타이머 콜백을받은 마샬링 작업을 수행 합니다.


4
타이머 콜백이 새 스레드에서 실행되도록하려면 System.Threading.Timer또는 System.Timers.Timer? 를 사용해야합니다 .
CJ7

1
@ cj7 : 어느 쪽이든 할 수 있습니다.
Brian Gideon 2012 년

복잡한 유형 (사람)의 목록이 있고 각 사람 안에 시간을 갖고 싶다면? 첫 번째 사람 메서드를 호출하면 두 번째 사람은 첫 번째 사람이 경과 된 이벤트를 끝낼 때까지 기다려야하기 때문에 동일한 스레드 (모든 사람)에서 실행해야합니다. 할 수 있습니까?
Leandro De Mello Fagundes 2014 년

4
모두 ... System.Timers.Timer두 가지 작동 모드가 있습니다. 임의로 할당 된 스레드 풀 스레드에서 실행되거나 ISynchronizeInvoke인스턴스를 호스팅하는 모든 스레드에서 실행될 수 있습니다 . 나는 그것을 더 명확하게하는 방법을 모른다. System.Threading.Timer원래 질문과 거의 관련이 없습니다.
Brian Gideon 2014

@LeandroDeMelloFagundes 당신 lock은 그것을 사용할 수 없습니까 ?
Ozkan

24

각 elapsed 이벤트는 이전 Elapsed가 아직 실행되지 않는 한 동일한 스레드에서 발생합니다.

그래서 그것은 당신을 위해 충돌을 처리합니다

이것을 콘솔에 넣어보십시오

static void Main(string[] args)
{
    Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
    var timer = new Timer(1000);
    timer.Elapsed += timer_Elapsed;
    timer.Start();
    Console.ReadLine();
}

static void timer_Elapsed(object sender, ElapsedEventArgs e)
{
    Thread.Sleep(2000);
    Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
}

당신은 이와 같은 것을 얻을 것입니다

10
6
12
6
12

여기서 10은 호출 스레드이고 6과 12는 bg elapsed 이벤트에서 발생합니다. Thread.Sleep (2000)을 제거하면; 당신은 이와 같은 것을 얻을 것입니다

10
6
6
6
6

충돌이 없기 때문에.

그러나 이것은 여전히 ​​당신에게 문제를 남깁니다. 5 초마다 이벤트를 실행하고 편집하는 데 10 초가 걸리는 경우 일부 편집을 건너 뛰려면 잠금이 필요합니다.


8
timer.Stop()Elapsed 이벤트 메서드의 시작 부분에를 추가 한 다음 Elapsed 이벤트 메서드 timer.Start()의 끝에를 추가하면 Elapsed 이벤트가 충돌하지 않습니다.
Metro Smurf 2011

4
timer.Stop ()을 넣을 필요는 없습니다. timer.AutoReset = false를 정의하면됩니다. 이벤트를 처리 한 후 timer.Start ()를 만듭니다. 충돌을 피하는 것이 더 좋은 방법이라고 생각합니다.
João Antunes

17

System.Timers.Timer의 경우 SynchronizingObject가 설정되지 않은 경우 별도의 스레드에 있습니다.

    static System.Timers.Timer DummyTimer = null;

    static void Main(string[] args)
    {
        try
        {

            Console.WriteLine("Main Thread Id: " + System.Threading.Thread.CurrentThread.ManagedThreadId);

            DummyTimer = new System.Timers.Timer(1000 * 5); // 5 sec interval
            DummyTimer.Enabled = true;
            DummyTimer.Elapsed += new System.Timers.ElapsedEventHandler(OnDummyTimerFired);
            DummyTimer.AutoReset = true;

            DummyTimer.Start();

            Console.WriteLine("Hit any key to exit");
            Console.ReadLine();
        }
        catch (Exception Ex)
        {
            Console.WriteLine(Ex.Message);
        }

        return;
    }

    static void OnDummyTimerFired(object Sender, System.Timers.ElapsedEventArgs e)
    {
        Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId);
        return;
    }

DummyTimer가 5 초 간격으로 실행되었는지 확인하는 출력 :

Main Thread Id: 9
   12
   12
   12
   12
   12
   ... 

따라서, OnDummyTimerFired는 Workers 스레드에서 실행됩니다.

아니요, 더 복잡합니다. 간격을 10ms로 줄이면

Main Thread Id: 9
   11
   13
   12
   22
   17
   ... 

이는 다음 틱이 실행될 때 OnDummyTimerFired의 이전 실행이 완료되지 않은 경우 .NET이이 작업을 수행하기 위해 새 스레드를 생성하기 때문입니다.

더 복잡하게하는 것은 "System.Timers.Timer 클래스는이 딜레마를 쉽게 처리 할 수있는 방법을 제공합니다. 공용 SynchronizingObject 속성을 노출합니다.이 속성을 Windows Form의 인스턴스 (또는 Windows Form의 컨트롤)로 설정하면 Elapsed 이벤트 처리기의 코드는 SynchronizingObject가 인스턴스화 된 동일한 스레드에서 실행됩니다. "

http://msdn.microsoft.com/en-us/magazine/cc164015.aspx#S2


좋은 예입니다. 나는 지금까지 몰랐고 여기저기서 잠금 장치를 설정해야합니다. 완전한.
Olaru Mircea 2015 년

그리고 타이머가 콘솔 응용 프로그램의 주 스레드에서 Elapsed 이벤트를 시작하려면 어떻게해야합니까?
jacktric

13

elapsed 이벤트가 간격보다 오래 걸리면 elapsed 이벤트를 발생시키는 또 다른 스레드를 생성합니다. 그러나 이에 대한 해결 방법이 있습니다.

static void timer_Elapsed(object sender, ElapsedEventArgs e)    
{     
   try
   {
      timer.Stop(); 
      Thread.Sleep(2000);        
      Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);    
   }
   finally
   {
     timer.Start();
   }
}

+1 간단하고 깨끗한 답변이지만 항상 작동하지는 않습니다. 오히려 타이머의 lock 또는 SynchronizingObject 속성을 사용해야합니다.
RollerCosta 2017 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.