System.Timers.Timer
是否在与创建它的线程不同的线程上运行?
假设我有一个带有计时器的类,每5秒触发一次。当计时器触发时,在 elapsed 方法中,某些对象被修改。假设修改这个对象需要很长时间,比如 10 秒。在这种情况下我是否可能会遇到线程冲突?
这要看情况。
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
并在需要时接收计时器回调后执行编组操作。
所以它会为你处理碰撞
尝试将其放入控制台
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 秒进行编辑,您需要一些锁定来跳过一些编辑。
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
static void timer_Elapsed(object sender, ElapsedEventArgs e)
{
try
{
timer.Stop();
Thread.Sleep(2000);
Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
}
finally
{
timer.Start();
}
}