Pthreads vs Win32 threads
An anonymous reader writes "It's interesting when different people have different opinions. While I was searching for something on Intel's website, I came across an article on why Windows threads are better than Posix threads. Curiously, I also came across this article on why Posix Pthreads are better that Win32 threads. The thing is, both of these articles are written by the same author!
So who is right (metaphorically speaking?), or what has changed since the first article was written?"
So who is right (metaphorically speaking?), or what has changed since the first article was written?"
Looks like he just switched the words in the articles...?
Both are on the Intel Website, so it would appear, if any $$$ was paid at all, it would have been Intel both times. Since Intel doesn't write operating systems, they really don't care which threading model succeeds.
Normally I'd agree with you. But if you read both articles, you realize that his intentions may be a bit less honorable. The Win32 article is the exact same article, but with the conclusions changed. Maturing opinions usually result in some discussion of why one has changed their mind (even if it's only mentioned in passing) and/or a deep explanation of what caused their mind to change. This smells more like an attempt to play both sides.
Of course, it could be that the author is simply not comfortable with writing. He could be looking to make a quick buck by taking a simple forum post of his and modifying it to reflect his current opinion. It's worth giving him the benefit of the doubt on, but I am certainly suspicious.
Javascript + Nintendo DSi = DSiCade
The thing is, they are just APIs. They both do just about the same thing. Asking which one is better is a pretty pointless question. I have always thought that the WaitFor* functions in Windows are quite nice, but frankly not that much of an advantage. It's quite rare that you actually need to wait for multiple objects of different types at the same time. Combine that with the fact that its semantics are slightly different for different objects (it destroys a thread, but only unlocks a mutex), and your program is that much more difficult to read. Of course, this is just comparing two APIs, a mostly pointless exercise, and says _nothing_ about implementation, which is quite a bit more important in terms of comparison. For example, Linux has completely changed its pthreads implementation since the switch from 2.4 to 2.6 (from LinuxThreads to NPTL), and programmers get the advantages without needing to change anything. In Windows, of course, we have no (or very little) idea of the implementation, except for what we can infer from the API, and performance tests. A third argument in this little debate could be to argue that one should just stay away from threads, period. I haven't successfully done it myself, since I find the threading paradigm useful, but using processes and non-blocking IO properly, one can avoid threads completely. Of course that's a bit easier to do with some of the Posix functions (eg. socketpair). But doing so will probably result in a more robust piece of software, and which scales better to multiple cores/processors. (Because processes do not share memory, so inter-thread cache misses will be minimized.)
That said, I find it quite creepy that this guy wrote these two articles with extremely similar wording for his introductions, making the exact opposite points. It is very strange. I wonder what his motivation was.
If you really want the Win32 model, it is easy enough to build it on top of PThreads, but there is no way to build PThreads on top of Win32.
Not strictly true. It is possible to build condition variables for Windows (Windows has Semaphores after all, which can be used to make just about anything), but the Windows threading model makes doing so far from easy. Please note that I'm not defending WinThreads, however, so much as clarifying this particular issue.
I've programmed in both and, other than the fact that you can't use sockets in the Wait... APIs (as you mention - but the BSD socket code is layered on top of native Win32, so you'd be passing objects created by two different subsystems to one subsystem's call, which shouldn't be expected to work), the rest of your post is either lack of experience with Win32 threads or subjective preference on your part.
Personally, I've found situations using each API where the functionality desired was very difficult to replicate in the other API. It usually requires some rethinking, is all. And, last I checked, there were things you could do in either API that weren't easily reproducable in the other API.
However, both APIs tend to evolve towards each other over time so it becomes less of an issue.
I seem to recall looking for--and not finding-- a win32 equivalent of pthread's condition variables. E.g., atomically release a mutex and then wait for an event. As the (excellent) pthread man page describes, this is a useful operation, often necessary to avoid a race condition.
What is the right way to do a pthread_cond_wait under win32?
> (e.g. memory is only copied if there is a write on Linux)
:)
Not just Linux.
I think this was the case with SVR4 and BSD4.3.
But that was a LONG time ago... memory gets fuzzy..
It's entirely probable that the first time I read about fork being copy-on-write was in Tannenbaum's yellow book, so it might even be in Minix.
It has certainly been the case as far as I'm aware in every non-toy Unix and UNIX for at least a decade.
But then again, I don't care, since I'm not a kernel developer.
Do daemons dream of electric sleep()?
It's not so cut and dry. I use a library I wrote to abstract away PThreads and Win32 Threads to give me a multi-platform interface, but I would say that each side has their upsides and downsides.
With pthreads, there's no way to combine socket i/o and thread notification. In Win32, I can have a thread waiting on both a socket and an event at the same time, allowing me to cleanly signal I/O blocked threads. This isn't the case with pthreads.
However, Win32 REALLY needs a way to handle negative mutexes (ie. taking more than one resource from a mutex at a time). I had to write my own, and it was a serious pain to make sure I didn't have any race conditions. All the articles I've seen on Win32 equivalents had race conditions, and that means that there means that there are probably developers trusting multi-threading primitives that they really shouldn't be./p>
If you think the pthread API is "bloated", or unintuitive for simple tasks, I'm going to have to go ahead and say you're a troll.
Bloated? On a Linux box I happen to have handy, libpthread.a is 208Kb.
Hard to use for simple tasks? Perhaps all of the *_init() functions are a bit daunting to the eyes. But does the Wikipedia article's example really look that bad?
I first used the pthread interface when I was only 15 years old. Even though I didn't fully understand the consequences of parallelization back then, I didn't feel like the library itself was enourmously comlex. I was able to whip out multi-threaded socket programs pretty quickly. Later I used Win32 threads. I found it to be basically the same.
I'm surprised more of the people complaining about the lack of WaitForMultipleObjectsEx() haven't mentioned POSIX semaphores. I saw one post about them, that's it.
Fork() is a hold over from a very long time ago. The very old machines did not have protected memory, and so each context switch meant dumping the contents of memory and loading a new process image. If you did a fork(), you just had to do the dump, but not the reload and you got a copy of the parent process's address space for free. Now, there are lots of hacks to try to make it fast (e.g. copy-on-write and vfork()). We keep using it because that's how UNIX has always worked, not because it's actually sensible.
I am TheRaven on Soylent News
You are talking mostly bollocks, probably because you don't program using the Windows API
And PThreads also gives you a fixed set of primitives. In some respects they are more powerful, in others they are weaker. This is a bullshit strawman.
WTF?
That brings me to the next point: in Unix there is no wait to wait on multiple synchronization primitives (as opposed to FDs) at once, and only a few primitives support a timed wait. In Windows you have WaitForMultipleObjectsEx. Look it up.
Let me be clear on the difference: in Windows the scheduler API for waiting on IO and synchronization primitives is fully integrated - you can wait on any combination of IO and sync objects in the same Wait call. You must of course be using Windows Async IO which uses Events rather than FDs as the waitable object. Under *nix you can wait for multiple condition variables, or multiple file descriptors, or a single mutex / thread / process. There is no way to link an FD to a condition variable.
In Windows you have Mutexes, Events and Semaphores. Events alone are sufficient to provide almost identical functionality to condition variables. You may have missed that memo.
I say "almost identical" because the underlying scheduler behaviour is slightly different, which makes it very difficult to perfectly emulate Pthreads on Win32. Read Strategies for Implementing POSIX Condition Variables on Win32.
Cough. Bullshit. Cough. Read Porting of Win32 API WaitFor to Solaris Platform to get a clue. It is possible to build Pthreads on top of Win32, and vice versa. Neither emulation is particularly efficient though.
The complaint about weakness in the Win32 scheduling API means that the author is using it incorrectly.
i-name =twylite [http://public.xdi.org/=twylite], see idcommons.net
Except that Win32 threading model only works on X86 (since it needs Win32, which only runs on X86), while Pthreads work everywhere. Pthreads therefore mean more competition for Intel, since the program using them can be made portable.
Forget magic. Any technology distinguishable from divine power is insufficiently advanced.
Windows has fork(), as well as all the other POSIX calls.
Originally, Windows NT had 4 separate APIs: the NT API, Win32, POSIX, and OS/2. The NT API was (and still is) the one used by the kernel. It has all the capabilities necessary to support the other three, and is never (well, rarely at least) used directly by applications. Win32 was, at the time, very new and hadn't seen wide use--it was designed as the primary API for Windows 95, and Microsoft wasn't yet sure whether it would catch on in the NT world.
The other two APIs have the same level of explicit support in the kernel as Win32, but there's a catch: you have to pick which subsystem you want to use for a given application. Win32 programs can't make calls to the POSIX subsystem, nor vice-versa. Over time, it became clear that only Win32 was being used for NT programming, and the other two APIs disappeared. The OS/2 API is, as far as I know, completely unavailable for modern versions of NT (i.e. 2000, XP, and Vista), while the POSIX API is still supported, but isn't available by default.
Check out Windows Services for Unix. This packages installs (and uses) the NT POSIX API.
For more information, read Microsoft Windows Internals
"With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea...."
RFC 1925
I ran both articles through diff and here's what it looks like: 1c1 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. 3c3 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. 5c5 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. 7c7 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. 9c9,15 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 conditio
Nobody program for Windows because he likes to program for Windows...
.NET (C#) at home, for fun. .NET is a genuine pleasure to work with: a better java than java, I say. It may be an asset of the evil empire, and it may not be multiplatform, but I find those to be its only weaknesses.
To the contrary. I work on Linux systems at work, and play on Windows, using
C//
A while back I had to write a server that would recieve concurrent network connections from different clients and then get some data from those clients and do some processing and then interact with a database, then when it's done the fork exits.
I ended up writing the whole thing using forks and no pthreads. My code was then subjected to a code review and one of the questions that came up in the review meeting was why I used fork and didn't do the implementation in pthreads. My arguement was one of complexity. I was challenged with the "fork is old technology, you should have used pthreads" and my response was "My implementation is easy to understand, oh and by the way, it works!"
Needless to say, my code has been in production for about a year and a half with no issues. I'm sure someone smarter than me could have wrote the whole using pthreads, but I'm just not sure what it would have gained them other than a slightly smaller memory footprint but at the price of increased complexity.
Yes Francis, the world has gone crazy.
I enjoy coding in Windows, and I do so for work, and on my own time. I enjoy being able to program multiple devices I happen to own -- Smartphones, Pocket PCs, desktops, servers, and soon probably a media center as well. I've done Java. I did VB6 for years. I've done a bit of C++. I've dabbled in Perl. I've written PHP when my arm's been twisted. I've been writing SQL against various datasources for years.
.NET. Maybe that's my lack of exposure to Java tools ... please feel free to show me where I'm deficient here. I started out in BASIC 20 years ago, I've run various Linux installations. I can still, quite honestly, say that I do in fact enjoy programming in Windows.
When it comes down to it, I find I'm able to most quickly create a pleasant GUI app in Visual Studio, using
I am, therefore you think.
Maybe it originated like that, but the fork/exec model of starting a new process has another really big benefit as compared to, say, Windows's CreateProcesses, which is that it's easier to control the execution environment of the child process.
Running as root and want to start the process with lower privileges? Do fork - setuid - exec. Want to open pipes to the child? Do pipe - fork - a couple calls in the child to connect the pipes to stdin/out - exec.
By contrast, Windows has to encapsulate anything you want to be able to do to set up the child's environment in the CreateProcess call. That's why it takes 9 arguments, one of which is a flags argument where there are 15 flags, and one of which is a pointer to a struct with 17 fields (one of which takes another 9 flags).
You hit the nail on the head. .NET and C# are awesome. But as long as Microsoft controls them, I will always be suspicious. I use it at work, but I am often hesitant to use it for my personal programming, mostly because of the multi platform issue.
My hope is that one day Java becomes as good. It certainly has the potential.
Joining to get the return value of a thread is pretty much a broken programming style on POSIX. Joins are only really used to recover resources. You should use some other mechanism to find out when a thread "terminates" (like condition variables) and use the pthread_join just to clean up the resources of the terminated thread.
Detached threads avoid this issue, and is the solution adopted by Mozilla for their cross-platform threading needs. It means you can't just return from a thread sensibly, but they make the conscious choice not to rely on this behavior.
While I'm sure the Windows way is convenient in some sense, I don't find the POSIX model deficient in any sense. Thread cancellation is a related issue as to why waiting for threads to exit is somewhat silly. Threads should signal when they've completed work, and you should be watching/waiting for this, not simply joining on threads waiting for them to terminate.
It's even easy enough to build a data type/procedure to do all this for you in just a few steps.
If the arguments in these articles are actually interchangeable, then either:
1) These arguments have no rational basis, and both are BS (unjustified opinions).
2) There are serious factual errors in at least one article.
I wonder which one of these is closer to the truth? I'm guessing it's #1. Either way, the author can no longer be taken seriously.
On the contrary, fork() is still sensible even today. Especially the copy-on-write implementations. fork() is ideal for saving memory in scripting languages.
Imagine a Perl program, which requires about 35 MB of memory (25 MB for the parsed code, 10 MB for actual work). The user wants to run 6 instances of the program. Without fork (that is, on Windows) the user will need 210 MB of memory. On Unix I can use fork() and the user will only need about 100 MB of memory because the parsed code is shared between instances.
This is not a hypothetical situation, I actually have a program that fits this description. A GNOME developer also recently developed a Python script which preloads PyGTK and forks instances, to save memory in PyGTK apps.
fork() is also ideal for making a "protected copy" of your program. Suppose that you have a web server which forks when a new client connected. If the child process crashes then it won't affect the parent process or the other children. I don't think you can do that in Windows easily.