我已经有一个运行System.Timers.Timer的Windows服务,它可以执行特定的工作。但是,我希望一些作品能够在同一时间运行,但是在不同的线程中。
我被告知要创建一个不同的System.Timers.Timer实例。它是否正确?这种方式是否可以并行运行?
例如:
System.Timers.Timer tmr1 = new System.Timers.Timer();
tmr1.Elapsed += new ElapsedEventHandler(DoWork1);
tmr1.Interval = 5000;
System.Timers.Timer tmr2 = new System.Timers.Timer();
tmr2.Elapsed += new ElapsedEventHandler(DoWork2);
tmr2.Interval = 5000;
tmr1
和tmr2
会在不同的线程上运行,以便DoWork1
和DoWork2
可以同时运行,即同时运行吗?
谢谢!
答案 0 :(得分:6)
这不是不正确的。
小心点。 System.Timers.Timer将为每个Elapsed事件启动一个新线程。当您的Elapsed事件处理程序耗时太长时,您会遇到麻烦。您的处理程序将在另一个线程上再次被调用,即使之前的调用尚未完成。这往往会产生难以诊断的错误。通过将AutoReset属性设置为false可以避免的事情。还要确保在事件处理程序中使用try / catch,吞没异常而不进行诊断。
答案 1 :(得分:5)
多个计时器可能表示多个线程。如果两个计时器滴答同时发生(即一个正在运行而另一个正在运行),那么这两个计时器回调将在不同的线程上执行,这两个线程都不是主线程。
但重要的是要注意,计时器本身根本不会“运行”在一个线程上。涉及线程的唯一时间是计时器的滴答或经过的事件触发。
另一方面,我强烈建议您不要使用System.Timers.Timer
。计时器的已用事件会压缩异常,这意味着如果异常逃脱了您的事件处理程序,您将永远不会知道它。这是一个臭虫隐藏。您应该使用System.Threading.Timer
代替。 System.Timers.Timer
只是System.Threading.Timer
的包装器,因此您可以获得相同的计时器功能,而不会隐藏错误。
有关详细信息,请参阅Swallowing exceptions is hiding bugs。
答案 2 :(得分:4)
tmr1和tmr2会在不同的线程上运行,以便DoWork1和DoWork2可以同时运行,即同时运行吗?
一开始,是的。但是,DoWork1
和DoWork2
在5秒内完成的保证是什么?也许您知道DoWorkX
中的代码并假设它们将在5秒的时间间隔内完成,但可能会发生系统处于负载状态,其中一个项目需要超过5秒。 这将打破你的假设,在这种情况下,即使你的后续开始时间是同步的,也有重叠的危险当前工作执行与工作执行仍然从最后一次开始运行。DoWorkX
将在后续滴答中同时开始。
但是,如果在DoWorkX
内禁用/启用相应的计时器,则您的开始时间将彼此不同步 - 最终可能会一个接一个地在同一个线程上进行调度。所以,如果你没事 - 随后的开始时间可能不同步 - 那么我的答案就在这里结束。
如果没有,这是你可以尝试的事情:
static void Main(string[] args)
{
var t = new System.Timers.Timer();
t.Interval = TimeSpan.FromSeconds(5).TotalMilliseconds;
t.Elapsed += (sender, evtArgs) =>
{
var timer = (System.Timers.Timer)sender;
timer.Enabled = false; //disable till work done
// attempt concurrent execution
Task work1 = Task.Factory.StartNew(() => DoWork1());
Task work2 = Task.Factory.StartNew(() => DoWork2());
Task.Factory.ContinueWhenAll(new[]{work1, work2},
_ => timer.Enabled = true); // re-enable the timer for next iteration
};
t.Enabled = true;
Console.ReadLine();
}
答案 3 :(得分:2)
有点儿。首先,查看System.Timers.Timer
:http://msdn.microsoft.com/en-us/library/system.timers.timer.aspx
您需要关注的部分如下所示:
如果SynchronizingObject属性为null,则Elapsed事件为 在ThreadPool线程上引发。如果处理了Elapsed事件 持续时间超过Interval,事件可能会再次提升 ThreadPool线程。在这种情况下,事件处理程序应该是 折返。
基本上,这意味着运行Timer
的操作并不是每个Timer
都有自己的线程,而是默认情况下,它使用系统ThreadPool
运行动作。
如果你想让事情同时运行(同时启动所有事情)但同时运行,你可以不只在已经过的事件上放置多个事件。例如,我在VS2012中试过这个:
static void testMethod(string[] args)
{
System.Timers.Timer mytimer = new System.Timers.Timer();
mytimer.AutoReset = false;
mytimer.Interval = 3000;
mytimer.Elapsed += (x, y) => {
Console.WriteLine("First lambda. Sleeping 3 seconds");
System.Threading.Thread.Sleep(3000);
Console.WriteLine("After sleep");
};
mytimer.Elapsed += (x, y) => { Console.WriteLine("second lambda"); };
mytimer.Start();
Console.WriteLine("Press any key to go to end of method");
Console.ReadKey();
}
输出是:
按任意键转到方法
第一个lambda。
睡3秒
睡觉后
第二个lambda
因此它不是同时执行连续。因此,如果您希望在每次执行计时器时“发生一系列事情”,则必须在Elapsed
处理程序中启动一系列任务(或使用Actions排队ThreadPool)。 可能多线程它们,或者它可能没有,但在我的简单示例中,它没有。
亲自尝试我的代码,说明发生的事情非常简单。