Use of Application.Doevents()

Use of Application.DoEvents()

From my experience I would advise great caution with using DoEvents in .NET. I experienced some very strange results when using DoEvents in a TabControl containing DataGridViews. On the other hand, if all you're dealing with is a small form with a progress bar then it might be OK.

The bottom line is: if you are going to use DoEvents, then you need to test it thoroughly before deploying your application.

What do DoEvents()'s in C# actually do?

DoEvents creates an additional message loop. It's just a loop that reads in a message, processes it, and then reads in the next message, until it gets a message that it should stop, or has no messages to process. Calling Application.Run creates the initial message loop for your application. Creating additional nested messages loops from within one of the handlers for these messages can cause all sorts of problems and so it should be avoided unless you're intimately familiar with what it does and under what circumstances you can use it correctly.

For the most part, rather than creating an additional message loop, your program should simply be asyncrhonous. Rather than blocking the current thread for a period of time, you should be using something like a Timer to execute a method after a period of time without blocking the current thread.

In .Net, timer and socket events run in their own threads

The actual waiting for the Timer's time to elapse, or for the socket to get a response, is done without the use of any thread at all. There is no thread sitting there sleeping, rather the operations are inherently asynchronous. As for the thread that is used to execute the event handlers, that will vary. Some Timers will use a thread pool, some the UI thread, and some are configurable and can do either. The socket is likely going to just be using a thread pool thread.

What to use instead of Application.DoEvents in my game?

I'd suggest to make that event handler async and use await Task.Delay() instead of Thread.Sleep():

private async void Game_Screen_KeyDown(object sender, KeyEventArgs e)
{
for (int i = 0; i < 500; i++)
{
if (e.KeyCode == Keys.Left)
{
cannonBox.Location = new Point(cannonBox.Left - 2, cannonBox.Top); //Changes location of cannonBox to a new location to the left
await Task.Delay(10); //Delays the movement by couple milliseconds to stop instant movement
}

if (e.KeyCode == Keys.Right)
{
cannonBox.Location = new Point(cannonBox.Left + 2, cannonBox.Top); //Changes location of cannonBox to a new location to the right
await Task.Delay(10); //Delays the movement by couple milliseconds to stop instant movement
}

if (e.KeyCode == Keys.Up)
{
createLaser(); //Calls the method whenever Up arrow key is pressed
}
}
}

This way, the control flow is returned to the caller and your UI thread has time to handle the other events (so no need for Application.DoEvents()). Then after (about) the 10ms, the control is returned and execution of that handler resumed.

There may more fine-tuning be necessary, because now of course you could manage to hit more keys while the method has not finished. How to handle that depends on the surroundings. You could declare a flag that signals current execution and refuses further method entries (no thread safety needed here as it's all happening sequentially on the UI thread).

Or instead of refusing re-entrance queue the keystrokes and handle them in another event, e.g. "idle" events (like Lasse suggested in the comments).


Note that an event handlers is one of the rare occasions where using async without returning a Task is ok.

Using the Application.DoEvents()

The Application.Dovents() method makes all pending messages processed. That can cause:

  1. Entering a code block twice before the current one finishes. (Let's assume that you navigate your browser with a button click. User clicks the button and while your code is waiting browser to copmlete the user clicked again. In that case Application.Doevents() will cause processing that method before stepping next line.)

  2. Interrupting critical codes. (Lets assume that you have a time consuming method and the user clicked close button. Your form will disappear but your code will continue to run. A real problem.

  3. Many more UnExpected results.

However I feel sometimes using this method is necessary and an easy solution like webbrowser which is difficult to use in multithreading (especially when its visible). If you have to use this method you should be sure that user and other things (timers, buttons, events vs) don't interrupt anything.
For a detailed discuss:Use of Application.DoEvents()

Is Application.DoEvents() a form of Multitasking?

I am pretty sure it is a very early, very primitive, Windows Forms only form of Multitasking

You are pretty close to correct on all counts except for your conjecture that it is for WinForms only. "DoEvents" precedes WinForms; it was present in Visual Basic long before WinForms was invented, and "pump the message queue" obviously precedes VB also. And it was a bad idea and easily abused then too.

Making the rest of said Event a continuation to be run later.

DoEvents doesn't really make anything into a continuation the way that say, await does. Whatever event is currently "in flight" when DoEvents is called has its state on the stack, and the stack is the implementation of continuation in this case. This is another point against DoEvents -- unlike await, it eats stack, and therefore can contribute to an overflow.

I just ran into a poster that insists it has "nothing to do with Multitasking".

You should ask the author for clarification then, since that certainly sounds wrong.

Application.DoEvents vs await Task.Delay in a loop

what are the actual differences between doing the two approaches, which is better and why?

The differences are in how messages are processed while waiting.

DoEvents will install a nested message loop; this means that your stack will have (at least) two message processing loops. This causes reentrancy issues, which IMO is the biggest reason to avoid DoEvents. There's endless questions about which kinds of events the nested message loop should process, because there's deadlocks on both sides of that decision, and there's no solution that's right for all applications. For an in-depth discussion of message pumping, see the classic Apartments and Pumping in the CLR blog post.

In contrast, await will return. So it doesn't use a nested message loop to process messages; it just allows the original message loop to process them. When the async method is ready to resume, it will send a special message to the message loop that resumes executing the async method.

So, await enables concurrency but without all the really difficult reentrancy concerns inherent in DoEvents. await is definitely the superior approach.

Basically there's an ongoing argument that DoEvents() is "better" because it doesn't consume any threads from the thread pool

Well, await doesn't consume any threads from the thread pool, either. Boom.

Application.DoEvents, when it's necessary and when it's not?

Application.DoEvents is usually used to make sure that events get handled periodicaly when you're performing some long-running operation on the UI thread.

A better solution is just not to do that. Perform long-running operations on separate threads, marshalling to the UI thread (either using Control.BeginInvoke/Invoke or with BackgroundWorker) when you need to update the UI.

Application.DoEvents introduces the possibility of re-entrancy, which can lead to very hard-to-understand bugs.

Is calling Application.DoEvents on main thread same as in ThreadPool?

The best way to do this is to run the form normally using Application.Run. When the test is done close the form or call Application.Exit on that thread.

DoEvents pumps events on the current thread. There can be multiple UI threads. DoEvents only affects the current one.

Your your unit test code could look like:

Form form = null;
var task = Task.Factory.StartNew(() => {
form = new Form(); //Run ctor on UI thread.
Application.Run(form);
}, LongRunning);

//Work with the form here.

form.Invoke(() => Application.Exit());
task.Wait();

This is just a sketch. Synchronization is missing and I'm sure there are other things left to solve for you.

LongRunning ensures that the GUI runs on a fresh thread each time. This prevents state leaks from test to test.

Basically, this is the standard UI thread plus worker thread model. Here, the unit test thread is the worker and the UI thread needs to be created. Normally, the UI thread would be the Main thread and the worker would be created.



Related Topics



Leave a reply



Submit