0
votes

Using Visual Studio 2015, visual C# windows application.

I need a timer that empties a queue in the main thread because some UI components are modified. I coded this:

    public Form1()
    {
        InitializeComponent();
        Q = new ConcurrentQueue<amsg>();
        timer = new System.Timers.Timer(100);
        timer.Elapsed += timer_Elapsed;
        timer.Enabled = true;
    }

    void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        clsUdpMsg msg;
        while (Q.TryDequeue(out msg)) handleRx(msg);
    }

but the Elapsed event executes in a worker thread...?

If I put a timer on the main form at design time the generated prototype is slightly different:

    private void timer1_Tick(object sender, EventArgs e)
    {
        amsg msg;
        while (Q.TryDequeue(out msg)) handleRx(msg);
    }

but this one DOES execute in the main thread context.

The question extends to any thread that creates a timer this way; I thought a timer created in a thread executes the elapsed event in the same thread context. Am I missing something?

Quick edit: I see where these are different timers, System.Timers.Timer and Windows.Forms.Timer. So - how do I reliably create timers that will execute in the thread where I create them?

2
they are 2 different Timers. The first is from System.Timers, Windows.Forms.Timer is the second - Ňɏssa Pøngjǣrdenlarp
There is 4 timers in .net, there also is System.Threading.Timer and System.Windows.Threading.DispatcherTimer too - Scott Chamberlain

2 Answers

5
votes

Yeah, you're just dealing with two separate Timer classes. System.Windows.Forms.Timer fires its Tick event on the main thread and System.Timers.Timer fires its Elapsed event on a background thread. The advantage of System.Windows.Forms.Timer is that it hides the threading; the advantage of System.Timers.Timer is that it doesn't require a message pump to work.

You ask, "How do I reliably create timers that will execute in the thread where I create them?" Well, this is easy when you have a Windows Forms GUI and are starting the Timer on the UI thread: just use System.Windows.Forms.Timer. If you don't have a message pump to take advantage of, you have to use a System.Timers.Timer and basically the only way to run something on the main thread when the timer fires its Elapsed event is to recreate something like a message loop in your main thread--have it sit, maybe waiting for a Monitor.Pulse or some other kind of notification from the timer thread. But this means your main thread won't be getting anything useful done in the meantime...

1
votes

This isn't a direct answer to your question, but a suggestion to avoid issues with threading of timers.

You could always use Microsoft's Reactive Framework (NuGet "Rx-WinForms").

Your code would look like this:

var subject = new Subject<clsUdpMsg>();

var subscription =
    subject
        .ObserveOn(this)
        .Subscribe(msg => handleRx(msg));

The subject replaces yourQ. Whenever you want to add a message you just call subject.OnNext(msg).

The .ObserveOn(this) marshalls the call back to the UI even if the call to subject.OnNext(msg) is made on a background thread.

If you want to stop the subscription just call subscription.Dispose().