System.Timers.Timer가 그것을 생성 한 스레드가 아닌 별도의 스레드에서 경과합니까?
5 초마다 실행되는 타이머가있는 수업이 있다고 가정 해 보겠습니다. 타이머가 실행되면 elapsed 메서드에서 일부 개체가 수정됩니다. 이 개체를 수정하는 데 10 초처럼 오랜 시간이 걸린다고 가정 해 보겠습니다. 이 시나리오에서 스레드 충돌이 발생할 수 있습니까?
System.Timers.Timer가 그것을 생성 한 스레드가 아닌 별도의 스레드에서 경과합니까?
5 초마다 실행되는 타이머가있는 수업이 있다고 가정 해 보겠습니다. 타이머가 실행되면 elapsed 메서드에서 일부 개체가 수정됩니다. 이 개체를 수정하는 데 10 초처럼 오랜 시간이 걸린다고 가정 해 보겠습니다. 이 시나리오에서 스레드 충돌이 발생할 수 있습니까?
답변:
대한 System.Timers.Timer :
타이머에 대한 MSDN 설명서는 다음과 같이 설명합니다.
System.Threading.Timer 클래스 는 ThreadPool 스레드에서 콜백을 만들고 이벤트 모델을 전혀 사용하지 않습니다.
따라서 실제로 타이머는 다른 스레드에서 경과합니다.
때에 따라 다르지. 는 System.Timers.Timer
두 가지 작동 모드가 있습니다.
SynchronizingObject
가 ISynchronizeInvoke
인스턴스로 설정된 경우 Elapsed
동기화 개체를 호스팅하는 스레드 에서 이벤트가 실행됩니다. 보통 이러한 ISynchronizeInvoke
경우는 보통 오래된 이외의 아무것도 없습니다 Control
그리고 Form
우리는 모든 잘 알고있는 것으로 인스턴스. 따라서이 경우 Elapsed
이벤트는 UI 스레드에서 호출되며 System.Windows.Forms.Timer
. 그렇지 않으면 실제로 ISynchronizeInvoke
사용 된 특정 인스턴스 에 따라 다릅니다 .
경우 SynchronizingObject
IS 그때는 null Elapsed
이벤트가 불려 ThreadPool
스레드 그리고 그것은 유사 동작 System.Threading.Timer
. 사실, 실제로는 System.Threading.Timer
이면에서를 사용하고 필요한 경우 타이머 콜백을받은 후 마샬링 작업을 수행 합니다.
System.Threading.Timer
또는 System.Timers.Timer
? 를 사용해야합니다 .
System.Timers.Timer
두 가지 작동 모드가 있습니다. 임의로 할당 된 스레드 풀 스레드에서 실행되거나 ISynchronizeInvoke
인스턴스를 호스팅하는 모든 스레드에서 실행될 수 있습니다 . 나는 그것을 더 명확하게하는 방법을 모른다. System.Threading.Timer
원래 질문과 거의 관련이 없습니다.
lock
은 그것을 사용할 수 없습니까 ?
각 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 초가 걸리는 경우 일부 편집을 건너 뛰려면 잠금이 필요합니다.
timer.Stop()
Elapsed 이벤트 메서드의 시작 부분에를 추가 한 다음 Elapsed 이벤트 메서드 timer.Start()
의 끝에를 추가하면 Elapsed 이벤트가 충돌하지 않습니다.
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가 인스턴스화 된 동일한 스레드에서 실행됩니다. "
elapsed 이벤트가 간격보다 오래 걸리면 elapsed 이벤트를 발생시키는 또 다른 스레드를 생성합니다. 그러나 이에 대한 해결 방법이 있습니다.
static void timer_Elapsed(object sender, ElapsedEventArgs e)
{
try
{
timer.Stop();
Thread.Sleep(2000);
Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
}
finally
{
timer.Start();
}
}