Why Windows Threads Are Better Than POSIX Threads

I've used both POSIX threads (Pthreads) and Windows threads APIs, and I believe that Windows has the better programming model of the two. While each threading method can create threads, destroy threads, and coordinate interactions between threads, the reason I make this claim is the simplicity of use and elegance of design of the Windows threads API. This is all from the perspective of multithreaded code developers or maintainers. Let me illustrate with a few examples.


Simplicity of data types. In Pthreads, each object has its own data type (pthread_t, pthread_mutex_t, pthread_cond_t, etc.) while, in Windows threads, there is pretty much just the one type: HANDLE. For Pthreads this means different functions are used for working with each object type. Reading and understanding Pthreads code written by someone else can be straightforward. However, this does mean that the programmer must know the number, order, and type of parameters for all the different functions. On the other hand, because of the use of the same type for different objects, there is a Create* function for each different object and a corresponding Release* function for most.


Perhaps the biggest advantage of a single object data type is that there is only the one function needed to make a thread block while waiting for an object: WaitForSingleObject. Thus, only one set of parameters needs to be known regardless of whether the code is waiting on a thread, a mutex, a semaphore, or an event. The related function, WaitForMultipleObjects, is just as simple to use and easily overcomes the problem of needing to wait for multiple thread terminations one function call at a time (pthread_join) that Pthreads requires. While some may say that using a single data type for many different objects can lead to confusion when used in WaitFor* calls, programmers should set the name of the handle such that it is readily apparent whether the code is expecting a thread termination, an event to be signaled, or a mutex to be released.


WaitForMultipleObjects functionality. Besides being able to block a thread waiting for multiple thread terminations in a single call, the programmer can actually wait for any out of a set of threads to terminate. That is, even when only one thread has completed, the WaitForMultipleObjects function can be set to return and indicate which thread triggered the return. If there is specific "clean up" processing that depends on the identity of the thread that finished, this can be done before returning to wait on the remaining threads. This clean up processing will be done in the most efficient order possible, soon after each thread terminates, no matter in what order this happens. Pthreads can perform similar post-processing, but will need to wait for the threads to terminate is some fixed order. So, even if the last thread finishes first, it must wait for all the post-processing of the previous threads to be completed.


Because different objects all use the HANDLE type, a call to WaitForMultipleObjects can be set to wait for any combination of threads, mutexes, semaphores, and/or events. This feature can give the programmer a flexibility that cannot be easily (if at all) duplicated in Pthreads. As an example, I've written Windows code that used an array to hold both thread and event handles to support a threaded search through data. The ideas was to signal the blocking thread if the item being looked for was found and when the searching thread terminated; if the object was not found, the searching thread terminated without setting the event. By waiting for either handle to cause WaitForMultipleObjects to return, a simple switch statement could determine if the item had been found (and process the data) plus perform some post-processing computation upon the termination of the searching thread (regardless of whether the search was successful).


Persistence of signals. To paraphrase a classic conundrum in terms of Pthreads: If a thread signals a condition variable and no other thread is waiting, does it make a sound? The signal is lost if there is no thread waiting on the condition variable. For this reason, it is mandatory to set up a while loop to test a conditional expression (used to prompt a signal), and that requires getting a mutex involved to protect the data within the conditional expression and any changes to that data, and don't even get me started about spurious wake ups. Sheesh!


For Windows threads, once an event is in the signaled state, it stays signaled. In other words, when that tree falls, it continues to scream in pain until someone comes along to hear it. It is up to the programmer to ensure the proper switching of Windows events from the signaled to unsignaled state. Part of this involves setting the attributes of the event correctly and being sure to reset manual events as needed. All in all, this seems more convenient and simple. Besides, once the specified conditions have been achieved in order to signal the event, it's not like they can be unachieved later. Under Pthreads, if you blink, you've missed it.


These are just some of the reasons that I think Windows threads is a better threading API than Pthreads. What do you think? Do you have other reasons to prefer Windows threads? Or do you think Pthreads is the better threading method? I'd like to hear about your preferences.


--clay


For more complete information about compiler optimizations, see our Optimization Notice.

65 comments

Top
Clay B.'s picture

Since I wrote this post, the Windows Thread model has added several features that are part of the Pthreads model. Thus the two models are even closer in functionality than they were back in 2006.

isz's picture

I am using Win32 System Threads since several years and have started to use pthread under Linux. I think from the user point of view are both very easy to use.

Win32 threads are a good thing when portability is not considered.

anonymous's picture

Whether Win32 or Pthreads seems "better" to you depends on whether your particular scenario can be better expressed with primitives offered by either API.

In a nutshell: Win32 threads (prior Vista) don't have condition variables but do have WaitForMultipleObjects(). Pthreads are the opposite - there are condition variables but no WaitForMultipleObjects().

Condition variables are a different concept from events and are used in different scenarios. They are quite hard to implement correctly under pre-Vista Win32 (see http://www.cse.wustl.edu/~schmidt/win32-cv-1.html). Fortunately, Vista supports them natively. On the other hand, WaitForMultipleObjects() cannot be implemented on Pthreads easily AND correctly.

As of syntactic differences between Win32 and PThreads - there are much harder things in multithreading and programming in general for these differences to be of much importance. After working with CATIA V5's 10000 or so headers strong API all other APIs seem a bit simplistic to me ;)

anonymous's picture

pthread are open source! You can modify things that you don't like.
BTW: This means that its evolution will continue driven by TECNICAL consideration and not by commercial ones.

anonymous's picture

Windows have POSIX threads as well. It is called fibers.

anonymous's picture

Regarding Windows Threads being "fundamentally broken", the PulseEvent() method doesn't indicate "fundamental broken-ness".

PulseEvent() is a convenience function that turns out to be a bad idea because it overlooked a race condition due to kernel async procedure calls.

It's also a bad idea because it fails to use a separate synchronization object to coordinate execution. Signaling the event indicates that it is now safe for whichever thread was released to continue execution. If you need to know which thread was released, that's a piece of shared state that itself requires synchronization. In other words, either the thread that was released is implicit (e.g., only a single thread could be waiting to be released) or it shouldn't matter.

It shouldn't matter because in the case of a single purpose thread, it's next task is predetermined. In the case of a multipurpose thread, it's next task is shared state that will have its own separate synchronization mechanism.

anonymous's picture

It seems to me that the problem of critically missing a signal/pulse (because your thread was borrowed by the kernel at the time of the pulse) should be handled by using separate synchronization objects and pools for separate tasks.

If there is only a single thread that can perform a given task then it should communicate that back to the dispatcher via some other task specific shared state before the dispatcher proceeds.

If several threads can perform a given task they should be waiting on a task specific event. Missing a pulse in this case isn't critical (another thread will eventually get to it) but you may take a performance hit (if no other threads are available for the task).

If several threads are contending to perform several tasks then, imho, this multipurpose pool should be refactored into several single purpose pools. You already have an operating system, why are you trying to write another?

anonymous's picture

I found this article and particularly the comments to be very useful. I know pthreads and haven't bothered to learn windows threading for the simple fact of it not being portable. I was wondering(previous to reading) if it would be worth creating a higher level library and switch for usage of one or another depending on the architecture it was compiled on. From what I have been reading it doesn't sound like it would be worth my time to even learn windows threads. Since NT, windows has some(enough for me) POSIX support and from what it sounds like they both work equally well on the side of performance. My goal has changed to writing a higher level api for pthreads. As was mentioned higher level api's come at a cost. Of course my api will be easier for me to use I'm writing it for myself for that purpose. However I will lose a lot of flexibility and might introduce bugs. Its just to play around with threads a little. I might want to write my own c++ wrapper class I'm usually pretty good at this. It is just for fun, for now who knows it might become practical.

anonymous's picture

The fundamental difference between the two models is that POSIX only provides mutex and condition locks. Win32 provide a while lot more, the most important of which, for me, are those that allow synchronization between threads. POSIX doesn't do this at all.

Unix is designed for multiprocessing. Win32 is designed for multithreading. They can both do the other, but they aren't as good at it.

I happen to like multithreading and I'm most comfortable in a real-time environment where I can control how and when threads run. POSIX makes that really hard. Certainly the Win32 implementation has some fundamental flaws, but at least it has something. Is there anything that will let me easily do synchronization points on Unix? I'm thinking about looking into some pre-Unix structures like Semaphores. Is there anything a bit more modern and easier to use? I'm really only concerned with MacOS X at this point.

Pages

Add a Comment

Have a technical question? Visit our forums. Have site or software product issues? Contact support.