简体   繁体   中英

Invoke timer from background thread

What I want to do. I want to SomeMethod will be called periodically. Therefore, I want to timer will be started from backgroung thread after body of background thread method is passed. _timer.Start() was invoked, but TickHandler doesn't;

code:

using Timer = System.Windows.Forms.Timer;

class TestTimer
    {
        private Timer _timer;
        private Thread _thread;

        public TestTimer()
        {
            // create and initializing timer. but not started!
            _timer = new Timer();
            _timer.Tick += TickHandler;
            _timer.Interval = 60000; // 1 minute

            // create and start new thread
            _thread = new Thread(SomeMethod);
            _thread.Start();
        }

        private void TickHandler(object sender, EventArgs e)
        {
            // stop timer
            _timer.stop();

            //some handling

            // run background thread again
            _thread = new Thread(SomeMethod);
            _thread.Start();
        }   

        private void SomeMethod()
        {
            // some operations

            // start timer!
            TimerStart();
        }

        private void TimerStart()
        {
            _timer.Start();
        } 
    }

By monkey method I found if add Delegate like this

internal delegate void TimerDelegate();

And replace string

TimerStart(); 

with

Application.Current.Dispatcher.Invoke(new TimerDelegate(TimerStart), null);

all works fine. Somebody can explain me what is the trick?

You've got things mixed up a bit.

If you want a timer that fires on a background thread , you don't have to create a thread to start it (it doesn't matter which thread calls the Start method). Just use System.Timers.Timer , and each Elapsed event will occur on a thread-pool thread.

If you want a timer that fires on the UI thread , since it looks like you're using WPF, you should use System.Windows.Threading.DispatcherTimer , and not the Windows Forms timer you've been using. You should create the timer (ie call new ) on a particular UI thread, and every Tick event will occur on that thread. Again, it doesn't matter from which thread you call Start .

Here's an explanation of what's happening in your code: You're starting a Windows Forms timer on a non-UI thread. This kind of timer requires a message pump to be running on that thread so it can receive messages. Because it's a non-UI thread, there's no message pump. When you used the Dispatcher.Invoke method, you marshaled the creation of the timer back to the application's main UI thread, which made it work. But it is all quite redundant. If you want to keep the code as is, just replace the timer with a DispatcherTimer , and then you'll be able to remove the Invoke call.

Alternatively, if you're using .NET 4.5 you could use await/async to make this all much easier (be sure to call SomeMethod from the UI thread):

async Task SomeMethod(CancellationToken ct)
{
    while (!ct.IsCancellationRequested)
    {
        await Task.Run(() => DoAsyncStuff(), ct);

        DoUIStuff();

        await Task.Delay(TimeSpan.FromMinutes(1), ct);
    }
}

MSDN can explain it for you:

Note The Windows Forms Timer component is single-threaded, and is limited to an accuracy of 55 milliseconds. If you require a multithreaded timer with greater accuracy, use the Timer class in the System.Timers namespace.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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