In many cases, applications will want to communicate with other processes on many file descriptors at once, waiting for messages from many peers (these descriptors can be many things such as pipes, sockets or terminal input). This is typically the case of server software, but many applications that handle complex interactions end up having to work this way. A first solution to do this is to use one thread per communication channel, and let all the threads communicate via shared memory.
Another solution is provided in most unix implementations : the select system call. This system call allows a single thread to wait on multiple file descriptors for one to become ready for reading/writing. Therefore the core of a server program becomes a single thread that selects a peer to communicate with, reads its message, processes it, and loops around.
Advantages & inconvenients
There are several advantages to this method, in particular the fact that you don't need to allocate as many threads as you have clients, and the fact that the synchronization becomes much simpler (it actually is the kernel that does the synchronization, not the process).
The main inconvenient is that while the server is processing one messages, all the other clients must wait for their request to be processed. An intermediate solution could be designed, where the server software uses a fixed number of threads, for instance one that reads the requests and several that process them.
An implementation of the select system call basically does the following :
- Look if any of the descriptors is ready for reading/writing
- If so, return it
- Otherwise, wait on all descriptor objects
- When resumed, restart at 1.
This implementation supposes that your kernel has a sort of "wait_on_objects" function that pauses the current thread and waits for a "ready" event on any of the objects (basically the "ready" event for a pipe or a socket happens when the other end has written something and the buffer is no longer empty).
On recent unixes the select system calls accept a timeout argument, that causes the select system call to end even if no descriptor is ready once the timeout has passed.