.NET - 多个计时器实例意味着多线程?

时间:2013-08-01 18:26:27

标签: multithreading c#-4.0 .net-4.0 timer

我已经有一个运行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;

tmr1tmr2会在不同的线程上运行,以便DoWork1DoWork2可以同时运行,即同时运行吗?

谢谢!

4 个答案:

答案 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可以同时运行,即同时运行吗?

一开始,是的。但是,DoWork1DoWork2在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.Timerhttp://msdn.microsoft.com/en-us/library/system.timers.timer.aspx

的MSDN页面

您需要关注的部分如下所示:

  

如果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)。 可能多线程它们,或者它可能没有,但在我的简单示例中,它没有。

亲自尝试我的代码,说明发生的事情非常简单。