C# 计时器是否在单独的线程上运行?

问题描述 投票:0回答:5

System.Timers.Timer
是否在与创建它的线程不同的线程上运行?

假设我有一个带有计时器的类,每5秒触发一次。当计时器触发时,在 elapsed 方法中,某些对象被修改。假设修改这个对象需要很长时间,比如 10 秒。在这种情况下我是否可能会遇到线程冲突?

c# multithreading timer
5个回答
215
投票

这要看情况。

System.Timers.Timer
有两种操作模式。

如果

SynchronizingObject
设置为
ISynchronizeInvoke
实例,则
Elapsed
事件将在托管同步对象的线程上执行。 通常这些
ISynchronizeInvoke
实例只不过是我们都熟悉的普通旧
Control
Form
实例。 因此,在这种情况下,会在 UI 线程上调用
Elapsed
事件,其行为与
System.Windows.Forms.Timer
类似。 否则,它实际上取决于所使用的特定
ISynchronizeInvoke
实例。

如果

SynchronizingObject
为 null,则在
Elapsed
线程上调用
ThreadPool
事件,其行为与
System.Threading.Timer
类似。事实上,它实际上在幕后使用
System.Threading.Timer
并在需要时接收计时器回调后执行编组操作。


70
投票
对于

System.Timers.Timer 参见下面

Brian Gideon 的回答

对于

System.Threading.Timer

有关计时器的 MSDN 文档

指出:

System.Threading.Timer 类
使得 ThreadPool 线程上的回调

和 根本不使用事件模型。

所以计时器确实在不同的线程上流逝了。


25
投票

所以它会为你处理碰撞

尝试将其放入控制台

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 秒进行编辑,您需要一些锁定来跳过一些编辑。


17
投票

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 线程上执行的。 

不,进一步的复杂化 - 如果您将间隔减少到 10 毫秒,

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

这是因为,如果在触发下一个刻度时 OnDummyTimerFired 的上一个执行未完成,则 .NET 将创建一个新线程来完成此工作。

让事情变得更复杂,

“System.Timers.Timer 类提供了一种简单的方法来处理这种困境 - 它公开了一个公共 SynchronizingObject 属性。将此属性设置为 Windows 窗体的实例(或 Windows 窗体上的控件)将确保 Elapsed 事件处理程序中的代码在实例化 SynchronizingObject 的同一线程上运行。”

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


16
投票

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

	
© www.soinside.com 2019 - 2024. All rights reserved.