C#同步对象和计时器。计时器线程

C#同步对象和计时器。计时器线程,c#,.net,multithreading,timer,deadlock,C#,.net,Multithreading,Timer,Deadlock,嗨,我正在尝试使计时器的已过事件。计时器类在我的主线程上触发。 我仅限于VS2008、.NET3.5。。。 在本帖中: 据称,使用SynchronizingObject将使处理程序在拥有该对象的线程上执行 所以我试了一下: class MyTimer { private readonly Timer timer; public MyTimer(ISynchronizeInvoke synchronizingObject) { Console.Out.WriteLine(Th

嗨,我正在尝试使计时器的已过事件。计时器类在我的主线程上触发。 我仅限于VS2008、.NET3.5。。。 在本帖中: 据称,使用SynchronizingObject将使处理程序在拥有该对象的线程上执行

所以我试了一下:

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();
  }
}
但输出为:1,4,4,4

为什么呢?如何使已用处理程序在主线程上执行

我试着用 但这也无济于事:

  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);
      }
    };
  }
以及:

但是仍然没有成功。。。每次调用所需的时间都是错误的

将调试强制到调用中会导致invalidoperation:“在创建窗口句柄之前,无法对控件调用invoke或BeginInvoke。”

最后的办法是调查:
但这真的有必要吗?或者有更简单的解决方案吗?

我找到了一个使用Dispatchermer的解决方案 和Dispatcher.Run()以在新线程中启动消息泵。为了停止调度器并顺利退出服务,我使用了

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

建议的答案没有回答原来的问题,这就是为什么即使SynchronizingObject已正确设置为来自主线程的控件,System.Timers.Timer Expressed事件也没有在主线程上正确触发

我自己也遇到过这个问题,结果证明解决方案是因为控件还没有句柄,即使它已经正确构造。诀窍是将控件的Handle属性的值放入一个伪变量中,以便初始化它(请参阅)

但一旦这个问题得到解决,就会在原始poster的代码中产生一个新的问题,因为Console.ReadKey()正在阻塞,但已运行的处理程序需要在该线程上运行,所以现在出现了死锁。一种解决方案是执行Application.DoEvents(),尽管可能有更好的解决方案底线:同步回主线程时,不要阻止它

海报的原始代码以及为使其正常工作而进行的修改如下:

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
类MyTimer{
专用只读系统。计时器。计时器;
私有静态自动恢复事件elapsedOutputted=新自动恢复事件(false);
//private static AutoResetEvent mainReady=新的AutoResetEvent(true);
公共MyTimer(ISynchronizeInvoke synchronizingObject){
Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);
定时器=新系统定时器定时器定时器(1000);
timer.SynchronizingObject=同步对象;
timer.appeated+=timer\u appeated;
timer.Start();
}
无效计时器(对象发送器,System.Timers.ElapseDevenTargets e){
//timer.Stop();不需要
Console.Out.WriteLine(Thread.CurrentThread.ManagedThreadId);
elapsedOutputted.Set();
//线程。睡眠(2000);不需要
//timer.Start();不需要
}
静态void Main(字符串[]参数){
控件c=新控件();
IntPtr tempNotUsed=c.手柄;
var mytimer=新的mytimer(c);
对于(int运行=0;运行<10;运行++){
虽然(!elapsedOutputted.WaitOne(1000)){//这将导致死锁,但1000ms超时将释放它
Application.DoEvents();//不确定DoEvents是否是最好的主意,但它确实做到了
}//结束时
}//结束
}//末端总管
}//结束类

你是正确的应用程序。DoEvents不是最好的主意。更好的方法是启动消息泵。然后需要调用“关闭”主线程。然而,我认为OP的Dispatcher解决方案是更好的,因为它不会使您具有Winforms依赖性。
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