Вы не думаете о пуле потоков как о пуле. Когда «Поток 1» завершает свою работу в PreRender, он возвращается в пул для повторного использования в любых целях. Когда данные будут готовы, End
выберет случайный поток из пула потоков, закончит свою работу и отправит на него ответ. Этот случайный поток может быть новым потоком или тем же потоком, который выполнял работу ранее и был возвращен в пул.
Дело в том, что в окне между Begin
и End
этот поток был перенастроен на пул и мог обслуживать другие соединения. Это позволяет вам иметь больше одновременных соединений, чем одновременных потоков, которые может обработать ваша система, в синхронной версии, как только вы достигнете максимального количества одновременных потоков, вы больше не сможете обрабатывать новые запросы.
Позвольте мне подробно остановиться на том, что происходит.
Вот временная шкала для двух синхронных запросов на подключение, чтобы показать в качестве базовой линии, для простоты мы скажем, что каждый шаг занимает 1 мс для выполнения, кроме PreRender
, для завершения которого требуется 19 мс.
╔═══════╦════════════════════════╦══════════════════╦═════════════════╦═══════════════════╗
║ Time ║ Action (Connection #) ║ Open Connections ║ Running Threads ║ Available Threads ║
╠═══════╬════════════════════════╬══════════════════╬═════════════════╬═══════════════════╣
║ 0 ms ║ Connection Request (1) ║ 0 ║ 0 ║ 2 ║
║ 1 ms ║ PreInit (1) ║ 1 ║ 1 ║ 1 ║
║ 2 ms ║ Init (1) ║ 1 ║ 1 ║ 1 ║
║ 3 ms ║ InitComplete (1) ║ 1 ║ 1 ║ 1 ║
║ 4 ms ║ PreLoad (1) ║ 1 ║ 1 ║ 1 ║
║ 5 ms ║ LoadComplete (1) ║ 1 ║ 1 ║ 1 ║
║ 6 ms ║ PreRender (1) ║ 1 ║ 1 ║ 1 ║
║ 10 ms ║ Connection Request (2) ║ 1 ║ 1 ║ 1 ║
║ 11 ms ║ PreInit (2) ║ 2 ║ 2 ║ 0 ║
║ 12 ms ║ Init (2) ║ 2 ║ 2 ║ 0 ║
║ 13 ms ║ InitComplete (2) ║ 2 ║ 2 ║ 0 ║
║ 14 ms ║ PreLoad (2) ║ 2 ║ 2 ║ 0 ║
║ 15 ms ║ LoadComplete (2) ║ 2 ║ 2 ║ 0 ║
║ 16 ms ║ PreRender (2) ║ 2 ║ 2 ║ 0 ║
║ 25 ms ║ PreRenderComplete (1) ║ 2 ║ 2 ║ 0 ║
║ 26 ms ║ SaveState (1) ║ 2 ║ 2 ║ 0 ║
║ 27 ms ║ SaveStateComplete (1) ║ 2 ║ 2 ║ 0 ║
║ 28 ms ║ Render (1) ║ 2 ║ 2 ║ 0 ║
║ 29 ms ║ Send Response (1) ║ 1 ║ 1 ║ 1 ║
║ 35 ms ║ PreRenderComplete (2) ║ 1 ║ 1 ║ 1 ║
║ 36 ms ║ SaveState (2) ║ 1 ║ 1 ║ 1 ║
║ 37 ms ║ SaveStateComplete (2) ║ 1 ║ 1 ║ 1 ║
║ 38 ms ║ Render (2) ║ 1 ║ 1 ║ 1 ║
║ 39 ms ║ Send Response (2) ║ 0 ║ 0 ║ 2 ║
╚═══════╩════════════════════════╩══════════════════╩═════════════════╩═══════════════════╝
А вот график для асинхронной версии.
╔═══════╦════════════════════════╦══════════════════╦═════════════════╦═══════════════════╗
║ Time ║ Action (Connection #) ║ Open Connections ║ Running Threads ║ Available Threads ║
╠═══════╬════════════════════════╬══════════════════╬═════════════════╬═══════════════════╣
║ 0 ms ║ Connection Request (1) ║ 0 ║ 0 ║ 2 ║
║ 1 ms ║ PreInit (1) ║ 1 ║ 1 ║ 1 ║
║ 2 ms ║ Init (1) ║ 1 ║ 1 ║ 1 ║
║ 3 ms ║ InitComplete (1) ║ 1 ║ 1 ║ 1 ║
║ 4 ms ║ PreLoad (1) ║ 1 ║ 1 ║ 1 ║
║ 5 ms ║ LoadComplete (1) ║ 1 ║ 1 ║ 1 ║
║ 6 ms ║ PreRender (1) ║ 1 ║ 1 ║ 1 ║
║ 7 ms ║ Begin (1) ║ 1 ║ 0 ║ 2 ║
║ 10 ms ║ Connection Request (2) ║ 1 ║ 0 ║ 2 ║
║ 11 ms ║ PreInit (2) ║ 2 ║ 1 ║ 1 ║
║ 12 ms ║ Init (2) ║ 2 ║ 1 ║ 1 ║
║ 13 ms ║ InitComplete (2) ║ 2 ║ 1 ║ 1 ║
║ 14 ms ║ PreLoad (2) ║ 2 ║ 1 ║ 1 ║
║ 15 ms ║ LoadComplete (2) ║ 2 ║ 1 ║ 1 ║
║ 16 ms ║ PreRender (2) ║ 2 ║ 1 ║ 1 ║
║ 17 ms ║ Begin (2) ║ 2 ║ 0 ║ 2 ║
║ 25 ms ║ End (1) ║ 2 ║ 1 ║ 1 ║
║ 26 ms ║ PreRenderComplete (1) ║ 2 ║ 1 ║ 1 ║
║ 27 ms ║ SaveState (1) ║ 2 ║ 1 ║ 1 ║
║ 28 ms ║ SaveStateComplete (1) ║ 2 ║ 1 ║ 1 ║
║ 29 ms ║ Render (1) ║ 2 ║ 1 ║ 1 ║
║ 30 ms ║ Send Response (1) ║ 1 ║ 0 ║ 2 ║
║ 35 ms ║ End (2) ║ 1 ║ 1 ║ 1 ║
║ 36 ms ║ PreRenderComplete (2) ║ 1 ║ 1 ║ 1 ║
║ 37 ms ║ SaveState (2) ║ 1 ║ 1 ║ 1 ║
║ 38 ms ║ SaveStateComplete (2) ║ 1 ║ 1 ║ 1 ║
║ 39 ms ║ Render (2) ║ 1 ║ 1 ║ 1 ║
║ 40 ms ║ Send Response (2) ║ 0 ║ 0 ║ 2 ║
╚═══════╩════════════════════════╩══════════════════╩═════════════════╩═══════════════════╝
Теперь при работе с двумя или менее соединениями асинхронный режим не имеет реальной пользы, фактически он может быть даже немного медленнее из-за дополнительных накладных расходов.
Однако посмотрите, что происходит, когда у нас более двух одновременных подключений.
Синхронизация:
╔═══════╦════════════════════════╦══════════════════╦═════════════════╦═══════════════════╗
║ Time ║ Action (Connection #) ║ Open Connections ║ Running Threads ║ Available Threads ║
╠═══════╬════════════════════════╬══════════════════╬═════════════════╬═══════════════════╣
║ 0 ms ║ Connection Request (1) ║ 0 ║ 0 ║ 2 ║
║ 1 ms ║ PreInit (1) ║ 1 ║ 1 ║ 1 ║
║ 2 ms ║ Init (1) ║ 1 ║ 1 ║ 1 ║
║ 3 ms ║ InitComplete (1) ║ 1 ║ 1 ║ 1 ║
║ 4 ms ║ PreLoad (1) ║ 1 ║ 1 ║ 1 ║
║ 5 ms ║ LoadComplete (1) ║ 1 ║ 1 ║ 1 ║
║ 6 ms ║ PreRender (1) ║ 1 ║ 1 ║ 1 ║
║ 10 ms ║ Connection Request (2) ║ 1 ║ 1 ║ 1 ║
║ 11 ms ║ PreInit (2) ║ 2 ║ 2 ║ 0 ║
║ 12 ms ║ Init (2) ║ 2 ║ 2 ║ 0 ║
║ 13 ms ║ InitComplete (2) ║ 2 ║ 2 ║ 0 ║
║ 14 ms ║ PreLoad (2) ║ 2 ║ 2 ║ 0 ║
║ 15 ms ║ LoadComplete (2) ║ 2 ║ 2 ║ 0 ║
║ 16 ms ║ PreRender (2) ║ 2 ║ 2 ║ 0 ║
║ 20 ms ║ Connection Request (3) ║ 2 ║ 2 ║ 0 ║
║ 25 ms ║ PreRenderComplete (1) ║ 2 ║ 2 ║ 0 ║
║ 26 ms ║ SaveState (1) ║ 2 ║ 2 ║ 0 ║
║ 27 ms ║ SaveStateComplete (1) ║ 2 ║ 2 ║ 0 ║
║ 28 ms ║ Render (1) ║ 2 ║ 2 ║ 0 ║
║ 29 ms ║ Send Response (1) ║ 1 ║ 1 ║ 1 ║
║ 30 ms ║ PreInit (3) ║ 2 ║ 2 ║ 0 ║
║ 31 ms ║ Init (3) ║ 2 ║ 2 ║ 0 ║
║ 32 ms ║ InitComplete (3) ║ 2 ║ 2 ║ 0 ║
║ 33 ms ║ PreLoad (3) ║ 2 ║ 2 ║ 0 ║
║ 34 ms ║ LoadComplete (3) ║ 2 ║ 2 ║ 0 ║
║ 35 ms ║ PreRender (3) ║ 2 ║ 2 ║ 0 ║
║ 35 ms ║ PreRenderComplete (2) ║ 2 ║ 2 ║ 0 ║
║ 36 ms ║ SaveState (2) ║ 2 ║ 2 ║ 0 ║
║ 37 ms ║ SaveStateComplete (2) ║ 2 ║ 2 ║ 0 ║
║ 38 ms ║ Render (2) ║ 2 ║ 2 ║ 0 ║
║ 39 ms ║ Send Response (2) ║ 1 ║ 1 ║ 1 ║
║ 54 ms ║ PreRenderComplete (3) ║ 1 ║ 1 ║ 1 ║
║ 55 ms ║ SaveState (3) ║ 1 ║ 1 ║ 1 ║
║ 56 ms ║ SaveStateComplete (3) ║ 1 ║ 1 ║ 1 ║
║ 57 ms ║ Render (3) ║ 1 ║ 1 ║ 1 ║
║ 58 ms ║ Send Response (3) ║ 0 ║ 0 ║ 2 ║
╚═══════╩════════════════════════╩══════════════════╩═════════════════╩═══════════════════╝
Асинхронный:
╔═══════╦════════════════════════╦══════════════════╦═════════════════╦═══════════════════╗
║ Time ║ Action (Connection #) ║ Open Connections ║ Running Threads ║ Available Threads ║
╠═══════╬════════════════════════╬══════════════════╬═════════════════╬═══════════════════╣
║ 0 ms ║ Connection Request (1) ║ 0 ║ 0 ║ 2 ║
║ 1 ms ║ PreInit (1) ║ 1 ║ 1 ║ 1 ║
║ 2 ms ║ Init (1) ║ 1 ║ 1 ║ 1 ║
║ 3 ms ║ InitComplete (1) ║ 1 ║ 1 ║ 1 ║
║ 4 ms ║ PreLoad (1) ║ 1 ║ 1 ║ 1 ║
║ 5 ms ║ LoadComplete (1) ║ 1 ║ 1 ║ 1 ║
║ 6 ms ║ PreRender (1) ║ 1 ║ 1 ║ 1 ║
║ 7 ms ║ Begin (1) ║ 1 ║ 0 ║ 2 ║
║ 10 ms ║ Connection Request (2) ║ 1 ║ 0 ║ 2 ║
║ 11 ms ║ PreInit (2) ║ 2 ║ 1 ║ 1 ║
║ 12 ms ║ Init (2) ║ 2 ║ 1 ║ 1 ║
║ 13 ms ║ InitComplete (2) ║ 2 ║ 1 ║ 1 ║
║ 14 ms ║ PreLoad (2) ║ 2 ║ 1 ║ 1 ║
║ 15 ms ║ LoadComplete (2) ║ 2 ║ 1 ║ 1 ║
║ 16 ms ║ PreRender (2) ║ 2 ║ 1 ║ 1 ║
║ 17 ms ║ Begin (2) ║ 2 ║ 0 ║ 2 ║
║ 20 ms ║ Connection Request (3) ║ 3 ║ 0 ║ 2 ║
║ 21 ms ║ PreInit (3) ║ 3 ║ 1 ║ 1 ║
║ 22 ms ║ Init (3) ║ 3 ║ 1 ║ 1 ║
║ 23 ms ║ InitComplete (3) ║ 3 ║ 1 ║ 1 ║
║ 24 ms ║ PreLoad (3) ║ 3 ║ 1 ║ 1 ║
║ 25 ms ║ End (1) ║ 3 ║ 2 ║ 0 ║
║ 25 ms ║ LoadComplete (3) ║ 3 ║ 2 ║ 0 ║
║ 26 ms ║ PreRenderComplete (1) ║ 3 ║ 2 ║ 0 ║
║ 26 ms ║ PreRender (3) ║ 3 ║ 2 ║ 0 ║
║ 27 ms ║ SaveState (1) ║ 3 ║ 2 ║ 0 ║
║ 27 ms ║ Begin (3) ║ 3 ║ 1 ║ 0 ║
║ 28 ms ║ SaveStateComplete (1) ║ 3 ║ 1 ║ 1 ║
║ 29 ms ║ Render (1) ║ 3 ║ 1 ║ 1 ║
║ 30 ms ║ Send Response (1) ║ 2 ║ 0 ║ 2 ║
║ 35 ms ║ End (2) ║ 2 ║ 1 ║ 1 ║
║ 36 ms ║ PreRenderComplete (2) ║ 2 ║ 1 ║ 1 ║
║ 37 ms ║ SaveState (2) ║ 2 ║ 1 ║ 1 ║
║ 38 ms ║ SaveStateComplete (2) ║ 2 ║ 1 ║ 1 ║
║ 39 ms ║ Render (2) ║ 2 ║ 1 ║ 1 ║
║ 40 ms ║ Send Response (2) ║ 1 ║ 0 ║ 2 ║
║ 45 ms ║ End (3) ║ 1 ║ 1 ║ 1 ║
║ 46 ms ║ PreRenderComplete (3) ║ 1 ║ 1 ║ 1 ║
║ 47 ms ║ SaveState (3) ║ 1 ║ 1 ║ 1 ║
║ 48 ms ║ SaveStateComplete (3) ║ 1 ║ 1 ║ 1 ║
║ 49 ms ║ Render (3) ║ 1 ║ 1 ║ 1 ║
║ 50 ms ║ Send Response (3) ║ 0 ║ 0 ║ 2 ║
╚═══════╩════════════════════════╩══════════════════╩═════════════════╩═══════════════════╝
Обратите внимание, что в версии синхронизации соединение 3 пришло через 20 мс, но ему пришлось ждать до 30 мс, когда стал доступен поток для обработки запроса. Напротив, асинхронная версия возвращала потоки обратно в пул, ожидая завершения PreRender, поэтому соединение 3 могло немедленно начать обработку.
Таким образом, использование асинхронного режима не увеличивает количество транзакций в секунду, а, скорее всего, немного снижает его. Однако то, что он делает, это увеличивает количество «Максимальное количество одновременных транзакций» и увеличивает общую пропускную способность.
person
Scott Chamberlain
schedule
05.08.2014