Threads strike fear into the hearts of many programmers. UNIX’s process model is simple and well understood, but it is sometimes inefficient. Threading can often allow for substantial improvements in performance, at the cost of a little confusion. This article demystifies the POSIX thread interface, providing practical examples of threaded code for consideration.
Basic Use of Pthreads
2004-02-03 General Development 23 Comments
pthreads have made an enormous (potential) performance versus usability tradeoff. Most Unix systems provide only very rudimentary event multiplexing interfaces, namely select() and poll(), and those which support more exotic interfaces [i.e. kevent()] still operate solely upon file descriptors.
Unfortunately, pthread conditions do not have associated file descriptors. Consequently, it becomes impossible to multiplex both thread conditions and file descriptors without using another thread also associated with the given condition which enters the system call or resorting to using POSIX realtime signal queues and signal-driven I/O for everything.
The simple solution would be to assign file descriptors to conditions and provide a function/functions to mark/unmark them as ready for reading/writing. This would require some systemic changes, such as new system calls for manipulating conditions, but would make for much less messy code when threads enter system calls.
Win32 provides a standard events system which integrates with its threading. This system does have some ridiculous limitations, such as the inability to wait for more than 64 events simultaneously, but those limitations aside, the design is significantly more cohesive than pthreads.
Not to mention win32 also has true userland theads (called “fibers” by microsoft).
Extremely useful in some situations.
I really wish unix had these, as well as Win32’s much better and easier to use synchronization primitives and events.
Here’s a link to a simpler thread tutorial: http://www.llnl.gov/computing/tutorials/workshops/workshop/pthreads…
If you don’t know exactly what threads are, don’t use them. If you don’t know the issues surrounding threads implementations in detail, don’t use them. If you don’t know the shortcomings and actual performance issues surrounding threads, don’t use them.
If you cannot make a clear case for coding with threads with a slick implementation, you will get killed debugging.
If you want to look at somthing REALLY cool to compltely replace threads you should look at this:
It is a method that simulates threads for asyncrounus IO but really uses epoll underneath. That way you can use threads instead of the god-aweful events type architecture, but still reap the rewards of performance
You’re just being silly now.. There are very easy and often used solutions for this, generally in a wakeup mechanism for the event blocking point. This can be trivially implemented by adding the event to a synchronized queue and signalling the event multiplexor with a synchronized write to a pipe where the event multiplexer polls on. As soon as the event multiplexor wakes up, it can check the thread event queue and process all events which happened until now.
No signals, extra systemcalls, realtime extensions or whatever are required. You’re merely attempting to spread FUD about unix and pthreads.
A novel approach, but to those of us concerned with portability it’s completely useless.
For transactional servers, I prefer to use a dispatcher thread which waits for events on all active sockets simultaneously using the best mechanism available (i.e. Win32 completion ports, kqueue, epoll, /dev/poll, sigio, poll, select) and pulls an idle worker thread from a pool whenever an event occurs. On any platform which supports an O(1) event mechanism, which now includes Win32, MacOS X Panther, Linux 2.4/2.6, Solaris, Irix, HP-UX, FreeBSD, OpenBSD, NetBSD, the dispatcher thread spends almost no time entering and leaving system calls besides the amount of time it takes to do the context switch (as opposed to select/poll which require time to validate and process the incoming list of events to check for and also time to process the file descriptor table checking for the events and construct the results data), and will often support fetching results for multiple file descriptors at once, so in theory the dispatcher thread itself will never become bogged down from I/O operations and therefore server responsiveness should always be quick.
Another advantage to that you only need one thread for each transaction you wish to handle concurrently. With this approach you can service upwards of 10,000 connections with 100 threads, which is hardly noticible on any OS which supports O(1) thread selection.
The biggest advantage of this approach is that you can attain massive scalability across all major popular platforms (Win32, MacOS X Panther, Linux 2.4/2.6, Solaris, Irix, HP-UX, FreeBSD, OpenBSD, NetBSD) while using 99% identical code.
Bottom line: You don’t have to sacrifice portability for performance.
You’re just being silly now.. There are very easy and often used solutions for this, generally in a wakeup mechanism for the event blocking point. This can be trivially implemented by adding the event to a synchronized queue and signalling the event multiplexor with a synchronized write to a pipe where the event multiplexer polls on.
This is precisely the solution I used in pdtpd (http://pdtpd.sourceforge.net/) and I have only one word for it: ugly. A pipe is an interprocess communication mechanism, and thanks to the deficiencies in the pthreads design I’m using it as an event signaling mechanism.
If I merely want to signal an event with the pipe, what am I supposed to write to it, an arbitrary byte? Doesn’t that seem… ugly to you?
Probably the best solution to the problem I mentioned has been provided by kqueues, which are not only 100% thread aware and support concurrent use by multiple threads, but can also be used to monitor signals in addition to file descriptor events, and file descriptor events can include VFS events for open files, similar to what’s provided by Linux’s F_NOTIFY based VFS monitoring, used by programs like FAM and dnotify.
Unfortunately, the Linux developers seem to have a huge case of NIH, and persisted in developing their own Linux-specific interface for synchronous O(1) event monitoring, epoll.
Nope, i do not thing this is ugly. It is the way this was intended to be. Afterall, why, if you can already trivially solve this problem in an efficient way, add a redundant
interface to integrate thread events with descriptor events?
And please let me know why, if you were aware of this wakeup solution, make such ridiculous suggestions on how to bypass this with signals and extra systemcalls. The only reason i can think of is to spread FUD.
mab im missing something im not that good with programming.
but if you just want to signal wouldnt a semafor be better than a pipe?
and if not it would be nice to know as im said im not that good but im trying to learn
Nope, i do not thing this is ugly. It is the way this was intended to be.
It’s intended that a pipe be used as an event signaling mechanism within a single process itself? Doesn’t that go against the whole idea of “Inter-Process Communication”?
Afterall, why, if you can already trivially solve this problem in an efficient way, add a redundant
interface to integrate thread events with descriptor events?
It’s not a redundant interface… pthread conditions are already provided by the pthreads API, but there are a number of situations where they’re inadequate for the precise reason I mentioned.
By your logic, why have pthread conditions at all? Why not use poll() in place of pthread_cond_wait() or pthread_cond_timedwait() and pipes in place of pthread conditions?
And please let me know why, if you were aware of this wakeup solution, make such ridiculous suggestions on how to bypass this with signals and extra systemcalls.
Because those are the only ways to utilize pthread conditions in conjunction with active system calls. If we take your approach, and simply use pipes in place of pthread conditions, then yes, everything works.
The only reason i can think of is to spread FUD.
No, my point is that pthreads are an area where the POSIX API is not cohesive. You have two different mechanisms which can be used for thread synchronization, pthread conditions and pipes/sockets, one designed to be used as a thread synchronization mechanism and one designed for Interprocess Communication. If a situation arises where you need to wait for a file descriptor event and a pthread condition, you’re SOL. You’re forced to use a mechanism (an IPC object) for a drastically different purpose than its original design to duplicate functionality of an existing mechanism because that existing mechanism is inadequate.
So in other words, the design of pthreads conditions is so inadequate that you are forced to use pipes, which were designed for interprocess communication, to fit their role.
Do we not have a textbook example of a “hack” here?
mab im missing something im not that good with programming. but if you just want to signal wouldnt a semafor be better than a pipe?
Semaphores are thread synchronization mechanisms designed to provide configurable thread concurrency for a particular piece of code.
pthread semaphores are unimplemented on most platforms. When they are implemented they typically sit on top of pthread mutexes and conditions.
SVR4 semaphores are not thread safe on most platforms.
Also note that this does not address my original problem, which is determining when a particular condition is signaled when you’re already blocking inside a system call.
thanx for o good answer i se i haveto read up some more on that
I’m expecting another Linux zealot-esque response from you, with screams of “FUD! FUD! FUD!”. Before you do, let me say one thing…
Believe it or not I’m primarily a POSIX programmer, and certainly not a Windows zealot. Pointing out the implementation deficiencies of one threads implementation and lauding the superior implementation found in another is not “FUD”, and pointing out a hack around a particular design deficiency does not negate the fact that the design was deficient to begin with.
With the proliferation of largely vendor-specific event monitoring mechanisms (e.g. kqueue, epoll, sigio, /dev/poll) on otherwise POSIX compliant platforms, wouldn’t you say that the event handling mechanisms provided by POSIX are somewhat inadequate? The standard mechanisms (select, poll) monitor only file descriptors, which are one of three vectors by which events may be sent to a thread (including pthreads synchronization mechanisms and signals). Wouldn’t it be much more sensible for the API to provide a single mechanism for monitoring all three?
I tend to agree with you, Bascule; however I would be just as happy to see a kill()-type call which took a process ID and and a thread ID, and would direct the signal to be processed by only the given thread. Threads could set themselves to ignore or install handlers for the signals, just as processes do now. Perhaps with a bit of tweaking to interruptable system calls, and then implementing pthread_kill(), etc., on this signal mechanism would simplify the mechanism.
A further refinement would be to provide the source of the signal (pid_t, pthread_t) to any handlers or allow the handlers to differentiate between signals within the process or external to the process; the latter could be implemented with the former (modulo system call restarts).
This is already implemented as:
int pthread_kill(pthread_t thread, int sig);
The pthread_kill() function conforms to ISO/IEC 9945-1:1996 (“POSIX.1”)
There’s no portable way to reference a thread outside of the current process. pthread_t is typically a pointer to a structure describing the thread, which is freed when the parent thread calls pthread_join(). The alternative is to invoke pthread_detach() which causes the structure to be freed when the thread exits.
Threads could set themselves to ignore or install handlers for the signals, just as processes do now.
The pthread counterpart to sigprocmask() is pthread_sigmask().
You can use signal() or to define a signal handler callback, but the pthreads spec says you shouldn’t use pthreads functions inside of this. Fortunately there are a number of ways to receive signals without signal handlers, such as sigwait(), sigwaitinfo(), or kqueues on *BSD.
Threading is notorious for being platform dependent, but there are solutions, one of which is to encapuslate the thread implementation with a platform neutral interface, which is done in Java.
The single most important thing you will ever learn about multithreaded programming is how and when to use mutexes (locks). Even before you create your first thread, you should learn what data should be protected by a mutex, and how to use them properly so that you don’t end up with deadlocks or holding a mutex for much longer than you should (thus holding other threads up). If you’re vaguely interested in threaded work, learn about mutexes, and get into good habits – even if you don’t actually use them to begin with, it’s good to make sure that mutexes can be added in the appropriate places without rewriting everything you’ve done so far.
This is especially important when using libraries – are your libraries thread-safe? Do they lock global or shared data structures to prevent multiple simultaneous access? Get into the habits sooner rather than later.
>> If you don’t know exactly what threads are, don’t use them. If you don’t know the issues surrounding threads implementations in detail, don’t use them. If you don’t know the shortcomings and actual performance issues surrounding threads, don’t use them.
>> If you cannot make a clear case for coding with threads with a slick implementation, you will get killed debugging.
If you do not understand how to use threads, do not write a GUI program. Ever tried using Red Hat’s Up2date program with a high latency connection such as dialup or satellite? The window drawing blocks while waiting for the network connection to begin transferring data, which takes anywhere from 1 second to 30 minutes with my ISP. And that’s assuming your connection continues to be up the whole time up2date is running; if not, up2date will simply stall – meaning that it will fail to redraw the window, that the cancel button won’t work, and that when you kill the app you might have a hard time installing other packages until you reboot.
Developers that write code like this should be publicly flogged and then retrained.
exaclty what I was thinking, I really like using threads in Java too, in fact between threading objects and synchronizing static variables in these objects I was able to achieve the same thing as using pthreads.
Bascule (IP: —.atmos.colostate.edu) is one of the smartest folks around here, and you’re nothing in camparison. Piss off with you “FUD” comments, as they’re not helpful and WAY overused.