В одном приложении не могу создать более 1000 потоков. Причём эта перегрузка по количеству потоков влияет на ОС (возможно переполнение памяти или идентификаторов потоков). Допустим, я создаю 800 потоков. После завершения их работы мне нужно провести чистку памяти, а потом опять запустить 800 потоков. Весь вопрос в том, какую именно память нужно очистить.
Невероятно. Зачем стока потоков, ведь они скорость не увеличат. Начёт памяти - надо только память выделенную под стек освободить.
SysProger, Clerk Это что за новость про освобождение чего-то там после завершения потока? Ну да, хэндл надо бы закрыть поточный, а что освобождать то ещё? Какие-то новые теории тут слышу...
Код (Text): DWORD WINAPI thread(LPVOID) { return 0; } int main() { for (int i=0; i<100000; i++) { HANDLE hTH = CreateThread (0, 0, thread, 0, 0, 0); if (!hTH) { printf("Cannot create thread %d\n", i); } CloseHandle(hTH); } printf("ok\n"); return 0; } нормально отрабатывает и ниче освобождать не надо
Clerk, wsd Программу пишу в образовательных целях. Поясняю. Открываю свою прогу в task manager, там вижу следующее: "Число дескрипторов - 185517". Т.е. функция ExitThread не может уменьшить это число. А памяти уже недостаточно для дальнейшего запуска любого потока. Потоки других программ тоже запускаться не смогут до завершения моего приложения. Вот мне и нужно узнать, как выйти из этой ситуации, не завершая моего приложения.
Great В твоей программе потокам мало дано шансов на выполнение. Ведь как только ты его создаёшь, ты сразу же закрываешь его описатель.
leo Ну поставлю я "CloseHandle(GetCurrentThread())" вместо "return 0" в теле потока, но делу этим не поможешь.
SysProger ИМХО, вы по моему вообще не понимаете о чем говорите. GetCurrentThread() - возвращает псевдоописатель, а CloseHandle закрывает реальный описатель. Читайте внимательее пост leo и факинг мануал. А вообще, предельное кол-во потоков, которое вы сможее создать в рамках своего процесса, зависеть будет от доступной памяти и размера стека, которое выделяется на поток по умолчанию. Создать их можно хоть 25 тыс.. только смысла в этом нет никакого.
от того, что я закрываю описатель, поток не уничтожается. rtfm UPD: Код (Text): LONG j = 0; DWORD WINAPI thread(LPVOID) { InterlockedIncrement (&j); return 0; } int main() { for (int i=0; i<100000; i++) { HANDLE hTH = CreateThread (0, 0, thread, 0, 0, 0); if (!hTH) { printf("Cannot create thread %d\n", i); } CloseHandle(hTH); } printf("ok, j=%d\n", j); return 0; } Вывод: ok, j=99996 (последние 4 потока не успевают отработать, поскольку программа преждевременно завершается. если перед printf(ok) воткнуть Sleep(100) тогда все сойдется) Объект имеет два счетчика ReferenceCount и HandleCount. Первый инкрементируется, когда кто-то хочет использовать явный указатель на объект, а когда хотят открыть хендл на него, то инкрементируются оба. ReferenceCount >= HandleCount всегда; объект удаляется, если он не permament и если ReferenceCount==0 Так что закрытие хендла еще не влечет завершение потока
ты посчитай сколько памяти будет выделено толкьо для стека каждого потока. По-умолчанию размер стека равен 1 МБ. 1000 потоков - 1000 МБ! и это только для стека.
rpy3uH стек очень хитро устроен. мало того, что винда никогда не передает физические страницы памяти до первого обращения к виртуальному адресу, так еще и стек делается разрастающимся с помощью PAGE_GUARD. так что реально памяти расходуется на порядки меньше
asmfan Ага, а из ThreadRoutine управление куда передаётся и что происходит по команде ret или как выполняется ExitThread ? Если поток завершать через ZwTerminateThread, то память под TEB освобождать не надо, а вот под стек - придётся.
Clerk Повторяем матчасть: Стек не освобождается только при выходе по (Zw)TerminateThread, поэтому эту "бяку" нужно использовать только в крайнем случае. При нормальном выходе по ret или ExitThread система сама освобождает память под стек
Clerk Вообще да, правы. По ExitThread вызывается RtlFreeThreadActivationContextStack и LdrShutdownThread и происходит много разных вещей, но тут всё на натив апи, а по ret и, собсно, ExitThread ничего делать самому не надо. [added] leo обогнал на повороте =)
leo ret фактически возвращает управлению коду который сам вызовет ExitThread. Если создать поток с использованием NtCreateThread тогда ret вернет в никуда.