简体   繁体   English

C#SynchronizingObject和Timer.Timer线程

[英]C# SynchronizingObject and Timer.Timer thread

Hi i am trying to make the Elapsed event of the Timer.Timer class fire on my main thread. 嗨,我想在我的主线程上使Timer.Timer类的Elapsed事件发生。 I am restricted to VS 2008, .net 3.5... In this post: Do C# Timers elapse on a separate thread? 我被限制在VS 2008,.net 3.5 ......在这篇文章中: C#Timers是否在一个单独的线程上过去了? it is stated that using a SynchronizingObject will make the handler execute on the thread that owns the object. 据说使用SynchronizingObject将使处理程序在拥有该对象的线程上执行。

So I tried this: 所以我尝试了这个:

class MyTimer
{
  private readonly Timer timer;

  public MyTimer(ISynchronizeInvoke synchronizingObject)
  {
    Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);
    timer = new Timer(1000);
    timer.SynchronizingObject = synchronizingObject;
    timer.Elapsed +=
      delegate
        {
          timer.Stop();
          Thread.Sleep(2000);
          Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);
          timer.Start();

        };
    timer.Start();
  }
}

class Program
{
  static void Main(string[] args)
  {
    ISynchronizeInvoke syncObject = new Control();
    var mytimer = new MyTimer(syncObject);

    Console.ReadKey();
  }
}

But the output is: 1,4,4,4,4... 但输出是:1,4,4,4,4 ......

Why is that? 这是为什么? How do i make the Elapsed handler execute on the mainthread. 如何在mainthread上执行Elapsed处理程序。

I tried using SynchronizingObject for an event but that didn't help either: 我尝试将SynchronizingObject用于事件,但这也没有帮助:

  public static ElapsedEventHandler Wrap(ElapsedEventHandler original, ISynchronizeInvoke synchronizingObject) 
  {
    return (sender, args) =>
    {
      if (synchronizingObject.InvokeRequired)
      {
        synchronizingObject.Invoke(original, new object[] { sender, args });
      }
      else
      {
        original(sender, args);
      }
    };
  }

and: 和:

timer.Elapsed += Wrap(
      delegate
        {
          timer.Stop();
          Thread.Sleep(200);
          Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);
          timer.Start();
        }, 
        synchronizingObject);

But still no success... everytime InvokeRequired is false... 但仍然没有成功......每次InvokeRequired都是假的......

forcing debug into the invoke causes a invalidoperation: "Invoke or BeginInvoke cannot be called on a control until the window handle has been created." 强制调试进入调用会导致无效操作:“在创建窗口句柄之前,无法在控件上调用Invoke或BeginInvoke。”

last resort would be to look into: http://www.codeproject.com/Articles/12082/A-DelegateQueue-Class?msg=3655119#xx3655119xx but is that really necessary? 最后的选择是: http//www.codeproject.com/Articles/12082/A-DelegateQueue-Classmsg = 3655119#xxx3655119xx但是真的有必要吗? or is there some simpler solution? 还是有一些更简单的解决方案?

I found a solution using DispatcherTimer and Dispatcher.Run() to start the message pump in a new thread. 我找到了一个使用DispatcherTimer和Dispatcher.Run()的解决方案来在新线程中启动消息泵。 To stop the dispatcher and exit the service smoothly i used 为了停止调度员并顺利退出服务,我使用了

if (_currentDispatcher != null) _currentDispatcher.InvokeShutdown();
_thread.Join();

The proposed answer does not answer the original question, which is why the System.Timers.Timer Elapsed event does not properly fire on the main thread even though the SynchronizingObject is properly set to a control from the main thread. 建议的答案没有回答原始问题,这就是为什么System.Timers.Timer Elapsed事件没有在主线程上正确触发,即使SynchronizingObject正确地设置为主线程的控件。

I had this problem myself and it turns out the solution is due to the fact that the Control doesn't have a handle yet, even though it's been properly constructed. 我自己遇到了这个问题,事实证明解决方案是因为Control还没有手柄,即使它已经正确构建了。 The trick is to get the value of the Control's Handle property into a dummy variable so that it gets initialized (see http://blogs.msdn.com/b/jfoscoding/archive/2004/11/24/269416.aspx ) 诀窍是将Control的Handle属性的值转换为虚拟变量,以便初始化它(参见http://blogs.msdn.com/b/jfoscoding/archive/2004/11/24/269416.aspx

But once that problem is solved, it creates a new problem in the original poster's code in that there is now a deadlock because Console.ReadKey() is blocking but the Elapsed handler needs to run on that thread. 但是一旦问题得到解决,它就会在原始海报的代码中产生一个新问题,因为现在存在死锁,因为Console.ReadKey()正在阻塞但是Elapsed处理程序需要在该线程上运行。 One solution is to do Application.DoEvents(), though there may be better solutions out there. 一个解决方案是执行Application.DoEvents(),尽管可能有更好的解决方案。 Bottom line: When synching back to the main thread, DON'T BLOCK IT! 底线:当同步回主线程时,请勿阻塞它!

The poster's original code with modifications to get it working is below: 海报的原始代码经过修改以使其正常工作如下:

class MyTimer {
    private readonly System.Timers.Timer timer;
    private static AutoResetEvent elapsedOutputted = new AutoResetEvent(false);
    //private static AutoResetEvent mainReady = new AutoResetEvent(true);

    public MyTimer(ISynchronizeInvoke synchronizingObject) {

        Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);
        timer = new System.Timers.Timer(1000);
        timer.SynchronizingObject = synchronizingObject;
        timer.Elapsed += timer_Elapsed;
        timer.Start();
    }

    void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) {
        //timer.Stop(); not needed
        Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);
        elapsedOutputted.Set();
        //Thread.Sleep(2000); not needed
        //timer.Start(); not needed
    }

    static void Main(string[] args) {
        Control c = new Control();
        IntPtr tempNotUsed = c.Handle;
        var mytimer = new MyTimer(c);

        for (int runs = 0; runs < 10; runs++) {
            while (!elapsedOutputted.WaitOne(1000)) { //this will deadlock, but the 1000ms timeout will free it
                Application.DoEvents(); //not sure if DoEvents is the best idea, but it does the trick
            } //end while
        } //end for
    } //end Main
} //end class

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM