C# SynchronizingObject and Timer.Timer thread

11,905

Solution 1

I found a solution using DispatcherTimer and Dispatcher.Run() to start the message pump in a new thread. To stop the dispatcher and exit the service smoothly i used

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

Solution 2

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.

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. 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 )

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. One solution is to do Application.DoEvents(), though there may be better solutions out there. 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
Share:
11,905
user1728586
Author by

user1728586

Updated on June 05, 2022

Comments

  • user1728586
    user1728586 almost 2 years

    Hi i am trying to make the Elapsed event of the Timer.Timer class fire on my main thread. I am restricted to VS 2008, .net 3.5... In this post: Do C# Timers elapse on a separate thread? it is stated that using a SynchronizingObject will make the handler execute on the thread that owns the object.

    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...

    Why is that? How do i make the Elapsed handler execute on the mainthread.

    I tried using SynchronizingObject for an event but that didn't help either:

      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...

    forcing debug into the invoke causes a invalidoperation: "Invoke or BeginInvoke cannot be called on a control until the window handle has been created."

    last resort would be to look into: http://www.codeproject.com/Articles/12082/A-DelegateQueue-Class?msg=3655119#xx3655119xx but is that really necessary? or is there some simpler solution?