Создание потоков стоит дорого, и каждый поток, который является одновременно «живым», будет добавлять определенное количество служебной информации, даже если поток заблокирован, ожидая, что что-то произойдет. Если у программы Foo есть 1000 задач, которые нужно выполнить, и на самом деле все равно, в каком порядке они выполняются, возможно, будет возможно создать 1000 потоков и каждый поток будет выполнять одну задачу, но такой подход не будет ужасно эффективным. Второй альтернативой было бы, чтобы один поток выполнял все 1000 задач последовательно. Если бы в системе были другие процессы, которые могли бы использовать какое-либо процессорное время, которое Foo не использовал, этот последний подход был бы эффективным (и вполне возможно оптимальным), но если не было достаточно работы, чтобы держать все процессоры занятыми, процессоры могли бы тратить время на бездействие. В большинстве случаев оставить процессор на секунду бездействующим так же дорого, как потратить секунду процессорного времени (главное исключение - попытка минимизировать потребление электроэнергии, поскольку на холостом ходу процессор может потреблять гораздо меньше энергии, чем занятый). ).
В большинстве случаев лучшая стратегия - это компромисс между этими двумя подходами: иметь некоторое количество потоков (скажем, 10), которые начинают выполнять первые десять задач. Каждый раз, когда поток завершает задачу, пусть он начинает работать с другой, пока все задачи не будут завершены. При таком подходе накладные расходы, связанные с многопоточностью, будут сокращены на 99%, и единственными дополнительными затратами будет очередь задач, которые еще не были запущены. Поскольку запись в очереди, как правило, намного дешевле, чем поток (вероятно, менее 1% от стоимости и, возможно, менее 0,01%), это может дать действительно огромную экономию.
Одна из основных проблем, связанных с использованием очереди заданий, а не многопоточностью, заключается в том, что если некоторые задания не могут быть завершены до тех пор, пока не будут выполнены задания, находящиеся позже в списке, система может оказаться заблокированной, поскольку более поздние задания не будут выполняться до более ранних задачи выполнены. Если бы каждой задаче был выделен отдельный поток, эта проблема не возникла бы, поскольку потоки, связанные с более поздними задачами, в конечном итоге смогли бы завершиться и, таким образом, позволить более ранним продолжаться. Действительно, чем раньше были заблокированы более ранние задачи, тем больше процессорного времени будет доступно для запуска более поздних.