Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

It is the ideal activity for a thread, but you actually don't want too many threads hanging around because each thread costs additional memory (stack + bookkeeping) and some amount of bookkeeping complexity for the OS scheduler, assuming native threads. That cost can really add up over the entire system.


How many is "too many" tho, realistically?

In an OS like Windows, on "typical" hardware, the amount of memory per thread for stacks and bookkeeping the OS and/or userland allocate is usually negligible. In this context also remember that the reserved stack sizes per thread do not refer to physical memory but virtual memory (or "committed" vs "reserved"). A typical thread usually commits 1 page for the stack and the equivalent of up to 2 pages worth for any bookkeeping[0], etc, which would put the memory committed for the 7412 threads the author mentions in the ballpark of maybe 30-100 MiB of memory actually committed.

The OS scheduler should be good enough to distinguish between runnable/waiting (sleeping) threads, eliminating the need to even look at most threads (which will be in waiting state) until some signal arrives putting them back into a runnable state.

You only really get a problem when you have too many runnable threads (fighting over the available resources) or when too many threads keep rapidly toggling between runnable and waitable, as either case would potentially lead to a lot of expensive context switches (and the former case is an indication you're trying to do too much with the physical resources available).

[0] Tho some things may commit considerably larger amounts of stack space and/or bookkeeping space. E.g. if I remember correctly java8 commits 1MiB of stack space per thread by default, while e.g. java11 merely reserves that 1MiB.


The standard approach on Windows (and no reason also for linux, FreeBSD) is to have per-process thread pools. E.g. each process in a .net app has a default thread pool. Windows also has an async IO api that requires a thread pool of threads to handle the IO (handled in the Win32 API, so built into the core OS).

A single thread can then wait for events that would require some processing (IO or timers being the most common) and then it passes that unit of work to the thread pool to be processed. Windows even takes pains to ensure that the last thread to process a unit of work, might then also be used to process the next item in the queue of work to eliminate costly context switching, and invalidation of data/instruction cache.

Thread pools allow the best use of threading by: - limiting the number of threads a process creates/maintains. The thread pool itself can grow or shrink as demand changes. - limiting the number of threads that are in a runnable state by queueing work to the pool. The pool itself will be sized according to number of CPU cores, e.g. 2 * CPU cores.

The main idea behind tread pools is that any thread that's not doing useful work is a waste of OS resources, so they make sure that they are either always fed with work, or get destroyed when idle for too long. From an overall machine efficiency perspective this makes total sense.

Having hundred or even thousands of threads created and idle is massively wasteful and bad design, the OS should not be burdened with such housekeeping.

Note that Apple's GCD also works on the same principle, as does libev/libuv.


A beefy Linux server can handles a million threads fine. Of course, most of them need to be idle or you're going to be maxing out some other resource.

The number of threads should only become a bottleneck if you have less than 0.01% of them idle.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: