C#定时器是否在单独的线程上经过?

use*_*164 86 c# multithreading timer

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

让我们说我有一个定时器,每5秒触发一次.当计时器触发时,在已用方法中,某些对象被修改.让我们说修改这个对象需要很长时间,比如10秒.在这种情况下,我是否可能遇到线程冲突?

Bri*_*eon 181

这取决于.将System.Timers.Timer有两种操作模式.

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

如果SynchronizingObject为null,则在线程Elapsed上调用该事件ThreadPool,其行为类似于System.Threading.Timer.事实上,它实际上使用System.Threading.Timer幕后操作,并在收到定时器回调进行编组操作(如果需要).

  • 如果你想让计时器回调在新线程上执行,你应该使用`System.Threading.Timer`还是`System.Timers.Timer`? (3认同)
  • 每个人......`System.Timers.Timer`有两种操作模式.它可以在随机分配的线程池线程上运行,也可以在托管`ISynchronizeInvoke`实例的任何线程上运行.我不知道如何更清楚地说明这一点.`System.Threading.Timer`几乎没有(如果有的话)与原始问题有关. (3认同)

Jor*_*ren 56

对于System.Timers.Timer:

请参阅下面的Brian Gideon的回答

对于System.Threading.Timer:

有关定时器的MSDN文档说明:

System.Threading.Timer类在ThreadPool线程上进行回调,根本不使用事件模型.

确实,计时器在不同的线程上流逝.

  • 没错,但这是一个完全不同的课程.OP询问了System.Timers.Timer类. (19认同)
  • 嗯,是的,但事情并不那么简单.看我的回答. (4认同)
  • 哦,你是对的。http://msdn.microsoft.com/en-us/library/system.timers.timer.aspx 说“Elapsed 事件是在 ThreadPool 线程上引发的。” 我想从那里得出同样的结论。 (2认同)

Sim*_*mon 21

除非先前的Elapsed仍在运行,否则每个已过去的事件将在同一个线程中触发.

所以它为你处理碰撞

尝试把它放在控制台中

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);
}
Run Code Online (Sandbox Code Playgroud)

你会得到这样的东西

10
6
12
6
12
Run Code Online (Sandbox Code Playgroud)

其中10是调用线程,6和12是从bg逝去事件触发.如果删除Thread.Sleep(2000); 你会得到这样的东西

10
6
6
6
6
Run Code Online (Sandbox Code Playgroud)

由于没有碰撞.

但这仍然让你有问题.如果你每5秒触发一次事件并且需要10秒钟来编辑你需要一些锁定来跳过一些编辑.

  • 在Elapsed事件方法的开头添加`timer.Stop()`,然后在Elapsed事件方法结束时添加`timer.Start()`将使Elapsed事件不会发生冲突. (7认同)
  • 你不需要把timer.Stop()只需要定义timer.AutoReset = false; 然后在处理事件后创建timer.Start().我认为这是避免碰撞的更好方法. (3认同)

Swa*_*Jat 15

对于System.Timers.Timer,在单独的线程上,如果未设置SynchronizingObject.

    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;
    }
Run Code Online (Sandbox Code Playgroud)

如果DummyTimer以5秒的间隔触发,你会看到输出:

Main Thread Id: 9
   12
   12
   12
   12
   12
   ... 
Run Code Online (Sandbox Code Playgroud)

因此,如图所示,OnDummyTimerFired在Workers线程上执行.

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

Main Thread Id: 9
   11
   13
   12
   22
   17
   ... 
Run Code Online (Sandbox Code Playgroud)

这是因为如果在下一个tick被触发时没有完成OnDummyTimerFired的上一次执行,那么.NET将创建一个新线程来完成这项工作.

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

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


小智 12

如果经过的事件比间隔花费的时间长,它将创建另一个线程来提升已经过去的事件.但是有一个解决方法

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