Существует ли в Windows ограничение в 2000 потоков на каждый процесс на самом деле?


Перевод с блога The Old New Thing. Оригинал здесь.

Часто у меня спрашивают, почему нельзя создать более чем примерно 2000 потоков в процессе. Причина заключается не в том, что в Windows есть какое-то специальное ограничение. Просто не учитывается величина адресного пространства, которое использует каждый поток.

Поток состоит из некоторого объема памяти в режиме ядра (стек ядра и управление объектами), некоторой памяти в режиме пользователя (блок переменных окружения, локальная память потока, к примеру), плюс стек потока. (Или стеки, если вы работаете на Itanium системе.)

Как правило, ограничиваюим фактором является именно размер стека.

#include <stdio.h>
#include <windows.h>

DWORD CALLBACK ThreadProc(void*)
{
      Sleep(INFINITE);
      return 0;
}

int __cdecl main(int argc, const char* argv[])
{
      int i;
      for (i = 0; i < 100000; i++) {
            DWORD id;
            HANDLE h = CreateThread(NULL, 0, ThreadProc, NULL, 0, &id);
            if (!h) break;
            CloseHandle(h);
      }
      printf("Created %d threads\n", i);
      return 0;
}

Эта программа скорее всего напечатает значение около 2000 созданных потоков. Почему именно 2000?
Потому что по умолчанию размер стека, задаваемый компоновщиком равен 1Мб, и 2000 стеков по 1Мб равны примерно 2Гб, то есть тому объему, который доступен для программ в режиме пользователя.

Вы можете попробовать втиснуть больше потоков в свой процесс, уменьшив размер стека, что может быть сделано, либо при помощи опций компоновщика, либо вручную, переопределив значение размера стека, передаваемое в функцию CreateThread как описано в MSDN.

HANDLE h = CreateThread(NULL, 4096, ThreadProc, NULL, STACK_SIZE_PARAM_IS_A_RESERVATION, &id);

После такой модификации, мне удалось создать около 13.000 потоков. Несмотря на то, что это однозначно лучше, чем 2000, было бы наивно ожидать создания 500.000 потоков. (Поток использует 4Кб для стека в 2Гб адресном пространстве.) Но вы забываете о другом ограничении. Гранулярность адресного пространства составляет 64Кб, так что каждый стек занимает 64Кб адресного пространства даже тогда, когда только 4Кб из них используются. Ну и конечно, у вас нет полноценных 2Гб адресного пространства: системные DLL и прочие вещи также располагаются здесь.

Но главный вопрос, который возникает, когда кто-то спрашивает: "Чему равно максимальное число потоков, которое может создать процесс?" таков: "А почему вы создаете так много потоков, что это становится актуально?"

Модель "один поток на одного клиента" хороша там, где число клиентов достигает десятка или около того. Если вы собираетесь обрабатывать одновременно большее число клиентов, то вам следует перейти к модели, когда вместо назначения каждому клиенту отдельного потока, происходит выделение объекта. В Windows есть пул потоков и иные средства, помогающие преобразовать потоковую модель в модель работы с объектами.

Обратите внимание, что "волокна" (fibers) здесь особо не помогут, потому что они имеют стеки, а почти всегда именно адресное пространство, необходимое для стека, является ограничивающим фактором.

Реклама
Запись опубликована в рубрике Uncategorized. Добавьте в закладки постоянную ссылку.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s