2

我已经有一个运行特定工作的 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

4 回答 4

6

这不是不正确的。

当心。System.Timers.Timer 将为每个 Elapsed 事件启动一个新线程。当您的 Elapsed 事件处理程序花费太长时间时,您会遇到麻烦。您的处理程序将在另一个线程上再次调用,即使之前的调用尚未完成。这往往会产生难以诊断的错误。您可以通过将 AutoReset 属性设置为 false 来避免这种情况。还要确保在您的事件处理程序中使用 try/catch,异常会在没有诊断的情况下被吞下。

于 2013-08-01T18:37:35.203 回答
5

多个计时器可能意味着多个线程。如果两个计时器滴答同时发生(即一个正在运行,另一个触发),这两个计时器回调将在不同的线程上执行,这两个都不是主线程。

不过,重要的是要注意,计时器本身根本不会在线程上“运行”。唯一涉及线程的时间是计时器的滴答声或经过的事件触发时。

另一方面,我强烈建议您不要使用System.Timers.Timer. 计时器的已用事件会压制异常,这意味着如果异常从您的事件处理程序中逃脱,您将永远不会知道它。这是一个错误隐藏器。你应该System.Threading.Timer改用。System.Timers.Timer只是一个包装器System.Threading.Timer,因此您可以在没有隐藏错误的情况下获得相同的计时器功能。

有关详细信息,请参阅吞咽异常隐藏错误

于 2013-08-01T18:42:29.130 回答
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();
        }
于 2013-08-01T23:09:09.300 回答
2

有点儿。首先,查看 MSDN 页面:http System.Timers.Timer: //msdn.microsoft.com/en-us/library/system.timers.timer.aspx

您需要关注的部分引用如下:

如果 SynchronizingObject 属性为 null,则在 ThreadPool 线程上引发 Elapsed 事件。如果 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();
    }

输出是这样的:

按任意键转到方法的结尾

第一个拉姆达。

睡 3 秒

睡觉后

第二个拉姆达

所以它连续执行它们而不是同时执行。因此,如果您希望在每次计时器执行时发生“一堆事情”,则必须在处理程序中启动一堆任务(或将 ThreadPool 与操作排队)Elapsed。它可能会多线程,也可能不会,但在我的简单示例中,它没有。

自己试试我的代码,很容易说明发生了什么。

于 2013-08-01T18:46:44.870 回答