WaitFor/Object
ResetEvent(evt);
WaitForSingleObject(evt, INFINITE);
выполняется кучей тредов (от 2х до бесконечности), и они все висят на 1й строке. Отдельный тред делает SetEvent(). Вчера возникли страшные сомнения (тупил наверное, сегодня они уже рассеялись но я задумал написать сюда, и напишу уже) что строка 2 в некоторых тредах (после SetEvent-а в управляющем) может не выполниться. Вообщем от вас требуется подтвердить безосновательность сомнений :).
UPD Не подумал. При авторесете, будет "отпущен" только один поток...
UPD2: MSDN уверяет что любое количество потоков, вошедших в wait-функцию будут "отпущены"
[quote=MSDN]
Any number of waiting threads, or threads that subsequently begin wait operations for the specified event object by calling one of the wait functions, can be released while the object's state is signaled.
[/quote]
Any number ... can be released
Типа, может быть отпущено любое число тредов (а не то которое вы ожидаете). Да и вообще смущает "может".
Пожалуй разовью тему. Вообще требуется сделать следующее:
Есть код с циклом. Этот код выполняется кучей тредов. По окончанию итерации цикла каждый тред засыпает. Последний заснувший тред должен пробудить контрол-тред который воспользовавшись награбленным отпустит остальные треды на следующую итерацию и заснет сам. Я, по доброте душевной, начал решать эту задачу на семафорах (они вообщем-то для этого самое оно) но выяснилось (из теории - не проверял), что виндовские семафоры не могут принимать значения ниже 0. Почему мы имеем такой бред и где я видел "нормальные" семафоры - вопрос недели у меня (похоже то ли в unix, то ли в qnx) :). Теперь я сижу на ивентах и самостоятельно майнтэню счетчик. Вроде сносно, но ощущение пустоты не отпускает. Может я пропустил что-то в жизни?
На семафорах задача вполне решаема. Нужно просто при создании семафора задать количество потоков, управление которыми будет им контролироваться.
Если счетчик семафора имеет значение N,то он сможет отпустить N потоков.
Ага. А мне из N потоков нужно отпускать 1. Отпускать N потоков из одного разумнее как-раз ивентом в моем случае (N - не конкретное а любое, и отпустить я должен не определенное кол-во, а всех).
Пусть он декрементит 2й семафор, когда он примет значение 0 он должен ждать (вообще он должен создаться заблокированным, т.е. = 0). Теперь любой тред отпускающий семафор (до значения 1) отпустит мастер-тред, что недопустимо - мастер-тред должен освободиться последним еще работающим тредом, в общем случае любым.
Окей. Соглашусь.
Пусть он декрементит 2й семафор, когда он примет значение 0 он должен ждать (вообще он должен создаться заблокированным, т.е. = 0). Теперь любой тред отпускающий семафор (до значения 1) отпустит мастер-тред, что недопустимо - мастер-тред должен освободиться последним еще работающим тредом, в общем случае любым.
Мастер поток должен знать сколько раз ему ждать сигнала
семафора. Т.е. знать количество потоков.
Вот примерный псевдокод:
worker_ready = CreateSemaphore(init = 0, max = MAX(INT32))
master:
for(i in [1..N])
InitWorkerThread(i)
loop {
for(i in [1..N])
WaitFor(worker_ready)
SetEvent(event)
ResetEvent(event)
}
worker:
DoSelfInit()
Release(worker_ready)
while(WaitFor(event))
DoWork()
Release(worker_ready)
}
Дык мне тож не очень :). Ну ладно, теперь будем думать какой вариант выбрать с учетом уже накиданного на ивентах.
О! Вспомнил чем мне не нравится цикл - тупое N-1 раз абсолютно бессмысленное пробуждение мастер-треда. А это все-таки ресурсы, да и вапще. Зол на микрософт :).
Дык мне тож не очень :). Ну ладно, теперь будем думать какой вариант выбрать с учетом уже накиданного на ивентах.
Зная количество потоков можно отпустить их все заклинанием Release, увелчив счетчик семафора ровно на N.
Если рабочие потоки выполняются достаточно длительно, то N-раз проснуться мастер-потоку будет практически ничего не стоить.
Да, с этим никаких проблем.
Если рабочие потоки выполняются достаточно длительно, то N-раз проснуться мастер-потоку будет практически ничего не стоить.
Ну блин, он будет N-1 раз пробуждаться чтобы сделать i++ !! Мне жалко планировщик потоков :D. По моему это ахтунг.
Сейчас полазил по MSDN, в принципе можно воспользоваться Thread Pool API, доступным начиная с Windows Vista. К примеру, WaitForThreadpoolWaitCallbacks ожидает пока завершатся все колбэки пула.
Есть код с циклом. Этот код выполняется кучей тредов. По окончанию итерации цикла каждый тред засыпает. Последний заснувший тред должен пробудить контрол-тред который воспользовавшись награбленным отпустит остальные треды на следующую итерацию и заснет сам.
Thread Pool API, доступным начиная с Windows Vista. К примеру, WaitForThreadpoolWaitCallbacks ожидает пока завершатся все колбэки пула.
ух ты блин :). Интересно. Но правда нам до висты далеко.
for (;;)
{
DataCollecting();
if ( InterlockedDecrement(&uMaxThreads) )
{
WaitForSingleObject(...);
}
else
{
DataProceessing();
uMaxThreads = X;
PulseEvent(...);
}
}
Развлекайтесь.
PS: Событие с мануальным сбросом
выполняется кучей тредов (от 2х до бесконечности), и они все висят на 1й строке. Отдельный тред делает SetEvent(). Возникли страшные сомнения что строка 2 в некоторых тредах (после SetEvent-а в управляющем) может не выполниться.
Кстати. Отвечу сам на свой же вопрос: :)
Сомнения оказались абсолютно обоснованными, действительно возможна ситуация когда все кроме 1 треда уснут на проснувшись. Надо будет найти время пробежаться по Рихтеру/Руссиновичу за поиском точного механизма работы планировщика. Найду техническое объяснение - добавлю.
[COLOR="Gray"]Если второй строчкой вставить Sleep(0) можно видеть что все потоки действительно пробуждаются.[/COLOR]