https://geocar.sdf1.org/fast-servers.html fast-servers There's a network-server programming pattern which is so popular that it's the canonical approach towards writing network servers: Flowchart of network server design described below Flowchart of network server design described below ... This design is easy to recognise: The main loop waits for some event, then dispatches based on the file descriptor and state that the file descriptor is in. At one point it was in vogue to actually fork() so that each file descriptor could be handled by a different thread, but now "worker threads" are usually created that all perform the same task and rely on the kernel to schedule file descriptors to them. A much better design is possible because of the epoll and kqueue, however most people use these "new" system calls using a wrapper like libevent which just encourages the same slow design people have been using for over twenty years now. The design I currently use and recommend involves two major points: 1. One thread per core, pinned (affinity) to separate CPUs, each with their own epoll/kqueue fd 2. Each major state transition (accept, reader) is handled by a separate thread, and transitioning one client from one state to another involves passing the file descriptor to the epoll/kqueue fd of the other thread. Flowchart of improved network server design... This design has no decision points, simple blocking/IO calls, and makes simple one-page performant servers that easily get into the 100k requests/second territory on modern systems. Creating the thread pool Ask the operating system how many cores there are. Sometimes reserving some cores make sense, so let the user lower this number. If raising this number helps, then your state transitions are too complex and you will need to break them up. pthread_attr_t a; pthread_attr_init(&a);pthread_attr_setscope(& a,PTHREAD_SCOPE_SYSTEM); pthread_attr_setdetachstate(&a,PTHREAD_CREATE_DETACHED); t=sysconf(_SC_NPROCESSORS_ONLN); for(i=0;i