Как мне отладить странное поведение потоков?
Не совсем то, что вы сказали, но ответ почти всегда: понять код действительно хорошо, понять все возможные результаты и понять, какой из них происходит. Отладчик становится здесь менее полезным, потому что вы можете либо следовать за одним потоком и пропустить то, что вызывает сбой других потоков, либо следовать из родительского, в этом случае выполнение больше не является последовательным, и вы в конечном итоге повсюду.
Теперь о проблеме.
pSemaphore = CreateSemaphore(NULL, THREAD_COUNT, THREAD_COUNT, NULL);
Из документации MSDN :
lInitialCount [in]: начальный счет для объекта семафора. Это значение должно быть больше или равно нулю и меньше или равно lMaximumCount. Состояние семафора сигнализируется, когда его счет больше нуля, и не сигнализируется, когда он равен нулю. Количество уменьшается на единицу, когда функция ожидания освобождает поток, который ожидал семафор. Счет увеличивается на указанную величину путем вызова функции ReleaseSemaphore.
И здесь :
Прежде чем поток попытается выполнить задачу, он использует функцию WaitForSingleObject, чтобы определить, позволяет ли текущий счетчик семафора сделать это. Параметр времени ожидания функции ожидания установлен на ноль, поэтому функция немедленно возвращается, если семафор находится в состоянии без сигнала. WaitForSingleObject уменьшает число семафоров на единицу.
Итак, что мы здесь говорим, так это то, что параметр count семафора сообщает вам, сколько потоков может одновременно выполнять заданную задачу. Когда вы изначально устанавливаете свой счет на THREAD_COUNT
, вы разрешаете всем своим потокам доступ к «ресурсу», который в этом случае должен продолжаться.
Ответ, на который вы ссылаетесь, использует этот метод создания семафора:
CreateSemaphore(0, 0, 1024, 0)
Что в основном говорит, что ни одному из потоков не разрешено использовать ресурс. В вашей реализации семафор сигнализируется (> 0), поэтому все продолжается весело, пока одному из потоков не удастся уменьшить счетчик до нуля, после чего какой-то другой поток ожидает, пока семафор снова не будет сигнализирован, что, вероятно, не не происходит синхронно с вашими счетчиками. Помните, что когда WaitForSingleObject
возвращает, уменьшается счетчик на семафоре.
В приведенном вами примере, установка:
::ReleaseSemaphore(sync.Semaphore, sync.ThreadsCount - 1, 0);
Работает, потому что каждый из вызовов WaitForSingleObject уменьшает значение семафора на 1, и есть threadcount - 1
из них, что происходит, когда все threadcount - 1
WaitForSingleObject
s возвращаются, поэтому семафор возвращается к 0 и, следовательно, снова не сигнализируется, поэтому на следующем проходе все ждут, потому что никому не разрешен доступ к ресурсу сразу.
Короче говоря, установите начальное значение на ноль и посмотрите, исправит ли это.
Редактировать Небольшое объяснение: так что если подумать об этом иначе, семафор подобен n-атомным воротам. Обычно вы делаете следующее:
// Set the number of tickets:
HANDLE Semaphore = CreateSemaphore(0, 20, 200, 0);
// Later on in a thread somewhere...
// Get a ticket in the queue
WaitForSingleObject(Semaphore, INFINITE);
// Only 20 threads can access this area
// at once. When one thread has entered
// this area the available tickets decrease
// by one. When there are 20 threads here
// all other threads must wait.
// do stuff
ReleaseSemaphore(Semaphore, 1, 0);
// gives back one ticket.
Таким образом, мы используем семафоры не для тех целей, для которых они были разработаны.