We hired a company to convert an old VB6 DLL which controls some industrial machinery to C#. The old VB6 code had "pause" routines consisting of sleep calls sprinkled with DoEvents so that while sleeping, timer and socket events in the DLL would still get processed. The DoEvents were converted to
System.Windows.Forms.Application.DoEvents();
I'm not a VB6 programmer but my understanding is that VB6 is actually single-threaded and so a long sleep would shut down everything including timer and socket event handling.
When the code was converted to C# the pause routines looked like this . . .
public static void pauseit_ms(ref int milliseconds)
{
try
{
Sleep(milliseconds / 2);
System.Windows.Forms.Application.DoEvents();
Sleep(milliseconds / 2);
}
catch (Exception exc)
{
LogException("pauseit_ms", exc);
}
}
In .Net, timer and socket events run in their own threads (most of my work in this converted code has been to try to make it thread-safe!) so it's not obvious what the DoEvents() buys us. But the MSDN says
Calling this method causes the current thread to be suspended while
all waiting window messages are processed.
So should we leave these DoEvents() in so other kinds of events (not timer or socket callbacks) aren't blocked? Or are they superfluous in a .Net/C# context?
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.
VB6 DoEvents suspends processing of the current procedure (which is the only way this can happen), processes all messages in the application message queue (so events may fire), thus making your procedure reentrant. It then calls Windows API function Sleep(0). Windows then does it's message stuff. At the end of it your procedure is restarted.
The good thing is that while you get the problems of multithreading, you are still single threaded, so simple code like If InProc = True then Exit Function:InProc = True:...:InProc = False:End Function
will work as it can't be preempted half way through.
Generally, you don't want to use Application.DoEvents()
. A bad practice example of how it can be used would be to make sure your cursor changes to the wait cursor before continuing with a long running operation on the same thread (the operation should really run on a separate thread).
Cursor.Current = Cursors.WaitCursor;
//Makes sure this change takes effect instead of being blocked by the next line.
//NOT SUPER RELIABLE
Application.DoEvents();
//Do long work here
Cursor.Current = Cursors.Default;
In your specific case, it looks like they are trying to use it as a hack to fake the UI being a little more responsive than it actually is.
You should use this in a loop instead:
await System.Windows.Threading.Dispatcher.Yield()
Creates an awaitable object that asynchronously yields control back to
the current dispatcher and provides an opportunity for the dispatcher
to process other events.
It's much nicer and essentially creates lazy loading of UI for you.
*Processes all Windows messages currently in the message queue.*
http://msdn.microsoft.com/en-us/library/system.windows.forms.application.doevents%28v=vs.110%29.aspx
It allows Window Forms to process any messages waiting for it from the Window Message Pump.
So all your code does it wait half the time it is asked, then process messages and wait again.