Worker Threads

Synchronization

Any time you have state shared between two threads it is essential that you provide synchronization on accesses. I discuss a fair amount of this in our book, Win32 Programming, and don't plan to replicate that discussion here. What is odd, however, is the fact that for variables such as paused I don't provide any synchronization. How can I get away with this?

The answer is that synchronization is not required providing only one thread ever modifies the variable, at least in some restricted cases. In our examples, the main GUI thread modifies the variable paused, but all other threads, such as the worker thread, only read it. It is true that the worker thread might, by a single instruction, miss detecting it, but the idea here is that one additional loop of the worker thread won't matter anyway, because the user might have missed it by tens or hundreds of milliseconds.

It has been pointed out to me that even if only one thread modifies the variable (although several threads may use it), if it takes more than one instruction (or one memory cycle) to do it, synchronization is required. For example, if the value is a 64-bit value and two 32-bit instructions are used to store it (because you did not compile for a native Pentium instruction set), you  could have the modifying thread preempted after it has stored the first 32 bits (whichever, high or low, the compiler has chosen to do first) but not the second 32 bits. This is, in fact, correct. If you are modifying a scalar value of more than 32 bits, and the generated code requires more than two instructions to store the value, you must still do synchronization between the modifier and users of the value to ensure that you have not been victimized by this anomaly. Note that if the compiler generates a 64-bit store, there might not a be a problem. The Pentium bus is 64 bits wide, and synchronization is done at the hardware level. But if the value is not aligned so that a single memory cycle can store it (for example, a 32-bit value split across a 64-bit boundary), two memory cycles are required to complete the store, making it risky for a multiprocessor environment. Therefore, you should be careful about taking advantage of this feature. A Tip of the Flounder Fin to Chris Bond for pointing this out.

So what about that case where I set running to be TRUE before AfxBeginThread and set it FALSE just as the thread exited? That just violated my previous statement, didn't it? Well, yes, But note that in this case the synchronization still exists. The thread is terminating, and therefore any computation left to be done in the thread is about to complete. No other work will be done in the thread. The GUI thread will not start a new thread until the running flag is FALSE. Unless you've set the m_bAutoDelete member of the CWinThread to be FALSE, all cleanup including deleting the object will be handled automatically. So we can actually "get away with it". 

If you want to be totally correct and precise, the only valid solution is to have yet another thread waiting on your first worker thread, and when the first worker thread completes, the second worker thread starts up, sets running to FALSE, and then terminates itself. This is a little clumsy and is essentially overkill, but is formally correct.

Summary

Working with threads introduces some complications, but compared to the problems of dealing with PeekMessage is a much better generalization of the notion of parallel computation. The amount of care that has to be exercised is startling at first, but after you've done a few multithreaded applications it becomes almost a reflex. Learn to use threads. You will be better off in the long run.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“PHP is a minor evil perpetrated and created by incompetent amateurs, whereas Perl is a great and insidious evil perpetrated by skilled but perverted professionals.” - Jon Ribbens