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 is most useful for simple things like allowing a user to cancel a process after it has started, for example a search for a file. For long-running processes, yielding to the processor is better accomplished by using a Timer or delegating the task to an ActiveX EXE component.
The DoEvents function returns an Integer representing the number of open forms in stand-alone versions of Visual Basic, such as Visual Basic, Professional Edition. DoEvents returns zero in all other applications. DoEvents passes control to the operating system.
Excel VBA DoEvents – Example #2Step 1: Write the subprocedure of VBA DoEvents as shown below. Step 2: Directly open a For-next loop without defining a variable. Step 3: Choose a number range we will see running. Let the range be 1 to 20000 same as example-1.
DoEvents method has the same behavior as the DoEvents method. When you run a Windows Forms application, it creates a new form, which then waits for events to be handled. Each time the form handles an event, such as a button click, it processes all the code associated with that event. All other events wait in the queue.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With