Home > Net >  Sockets use threads instead of select()
Sockets use threads instead of select()

Time:10-30

I have a question about multi sockets. I know that I have to use select() for multi sockets. select() waits for a fd ... But why we need to use select() when we can create a thread for each socket and perform accept() on each one seperatly ? Is it even a bad idea ? Is it just about "too many sockets, too many threads so" or what ??

CodePudding user response:

It's true, you can avoid multiplexing sockets by instead spawning one thread for each socket, and then using blocking I/O on each thread.

That saves you from having to deal with select() (or poll() or etc); but now you have to deal with multiple threads instead, which is often worse.

Whether threads will be more of a hassle to manage than socket-multiplexing in your particular program depends a lot on what your program is trying to do. For example, if the threads in your program don't need to communicate/co-operate with each other or share any resources, then a multithreaded design can work well (as would a multiprocess design). On the other hand, if your threads all need to access a shared data structure or other resource, or if they need to interact with each other, then you've got a bit of a programming challenge on your hands, which you'll need to solve 100% perfectly or you'll end up with a program that "seems to work most of the time" but then occasionally deadlocks, crashes, or gives incorrect results due to incorrect/insufficient synchronization. This phenomenon of "meta-stability" is much more common/severe amongst buggy multithreaded programs than in buggy single-threaded programs, since a multithreaded program's exact flow of execution will be different every time you run it (due to the asynchronous nature of the threads with respect to each other).

Stability and code-correctness issues aside, there are a couple of other problems particular to multithreading that you avoid by using a single-threaded design:

  1. Most OS's don't scale well above a few dozen threads. So if you're thinking one-thread-per-client, and you want to support hundreds or thousands of simultaneous clients, you're in for some performance problems.

  2. It's hard to control a thread that is blocked in a blocking-socket call. Say the user has pressed Command-Q (or whatever the appropriate equivalent is) so it's now time for your program to quit. If you have one or more threads blocked inside a blocking-socket call, there's no straightforward way to do that:

    • You can't just call unilaterally call exit(), because while the main thread is tearing down process-global resources, one or more threads might still be using them, leading to an occasional crash
    • You can't ask the threads to exit (via atomic-boolean or whatever) and then call join() to wait for them, because they are blocking inside I/O calls and thus might take minutes/hours/days before they respond
    • You can't send a signal to the threads and have them react in a signal-handler, because signals are per-process, and you can't control which thread will receive the signal.
    • You can't just unilaterally kill the threads, because they might be holding resources (like mutexes or file handles) that would then remain unreleased forever, potentially causing deadlocks or other problems
    • You can't close down the threads' sockets for them, and hope that this will cause the threads to error out and terminate, as this leads to race condition if the threads also try to close down those same resources.
    • So even in a multithreaded design, if you want a clean shutdown (or any other sort of local control of a network-thread) you usually end up having to use non-blocking I/O and/or socket multiplexing inside each thread anyway, so now you've got the worst of both worlds, complexity-wise.
  • Related