Win32 Понимание семафора

Я новичок в многопоточности в Win32. И у меня есть задание с Semaphore. Но я не могу этого понять.

Предположим, что у нас есть 20 задач (каждая задача совпадает с другими задачами). Мы используем семафор, тогда есть 2 обстоятельства:

Во-первых, должно быть 20 дочерних потоков, чтобы каждый поток выполнял 1 задачу.

Or:

Во-вторых, будет n дочерних потоков. Когда поток завершает задачу, он будет обрабатывать другую задачу?

Вторая проблема, на которую я возражаю, заключается в том, что я не могу найти образцы для семафора в Win32 (API), кроме Consonle, которые я нашел в MSDN.

Не могли бы вы помочь мне с "задачей 20" и подсказать инструкцию по написанию семафора в приложении WinAPI (где разместить функцию CreateSemaphore()...)?

Ваше предложение будет оценено.


person suti    schedule 15.06.2013    source источник


Ответы (2)


Вы можете запустить поток для каждой задачи, что является распространенным подходом, или вы можете использовать «пул потоков», в котором потоки используются повторно. Это зависит от вас. В обоих сценариях вы можете использовать или не использовать семафор, разница только в том, как вы запускаете несколько потоков.

Теперь, что касается вашего вопроса, где разместить функцию CreateSemaphore(), вы должны вызвать ее, прежде чем запускать какие-либо дальнейшие потоки. Причина в том, что этим потокам нужен доступ к семафору, но они не могут этого сделать, если он еще не существует. Вы, конечно, можете передать его другим потокам, но это снова создаст проблему, как безопасно передать его без каких-либо условий гонки, чего должны избежать семафоры и другие примитивы синхронизации. Другими словами, вы только усложните ситуацию, создав проблему курицы и яйца.

Обратите внимание, что если это больше не поможет вам, вам, возможно, следует предоставить дополнительную информацию. Каковы цели? Что ты сделал сам до сих пор? Есть ли здесь связанные вопросы, которые вы читали, но которые не дали полного ответа на вашу проблему?

person Ulrich Eckhardt    schedule 15.06.2013
comment
Должен ли я поместить CreateSemaphore() в WinMain или WM_CREATE? - person suti; 15.06.2013
comment
Важно не где, а когда! Если бы я был на вашем месте, я бы на время забыл об окнах и использовал бы простое консольное приложение, хотя бы для простоты. - person Ulrich Eckhardt; 15.06.2013

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

Вам нужен класс SimpleQueue для объектов задач. Я предполагаю, что у вас уже есть один, вы можете легко его построить или что-то еще.

В ctor вашего класса «ProducerConsumerQueue» (или в main(), или в какой-либо фабричной функции, которая возвращает структуру *ProducerConsumerQueue, независимо от вашего языка), создайте SimpleClass и два семафора. Семафор QueueCount, инициализированный счетчиком 0, и семафор QueueAccess, инициализированный счетчиком 1.

Добавьте методы/memberFunctions/методы 'push(*task)' и '*task pop()' в ProducerConsumerQueue:

В «push» сначала вызовите API «WaitForSingleObject()» в QueueAccess, затем поместите *task в SimpleQueue, затем API ReleaseSemaphore() в QueueAccess. Это подталкивает *task потокобезопасным способом. Затем ReleaseSemaphore() для QueueCount — это будет сигнализировать обо всех ожидающих потоках.

В pop() сначала вызовите API «WaitForSingleObject()» для QueueCount — это гарантирует, что любой вызывающий поток-потребитель должен ждать, пока в очереди не появится * задача. Затем вызовите API «WaitForSingleObject()» в QueueAccess, затем извлеките задачу из SimpleQueue, затем API ReleaseSemaphore() в QueueAccess и верните задачу — этот потокобезопасно удаляет *задачу из очереди.

После создания очереди ProducerConsumerQueue создайте несколько потоков для выполнения задач. В CreateThread() передайте тот же *ProducerConsumerQueue в качестве «вспомогательного» параметра *void.

В функции потока приведите *void обратно к *ProducerConsumerQueue, а затем просто зациклитесь, вызвав pop() и затем запустив возвращенную задачу.

Хорошо, теперь ваш пул потоков готов к работе. Если вы хотите запустить 20 задач, создайте их в цикле и поместите в ProducerConsumerQueue. Затем потоки будут запускать их все.

Вы можете создать столько потоков, сколько хотите в пуле (в разумных пределах). Столько потоков, сколько ядер, разумно для задач, интенсивно использующих ЦП. Если задачи делают блокирующие вызовы, вы можете создать гораздо больше потоков для максимальной общей пропускной способности.

Полезным усовершенствованием является проверка на «null» в цикле функции потока после получения каждой задачи и, если это значение null, очистка выхода из потока, что завершает его. Это позволяет легко завершать потоки, помещая в очередь нули, упрощая закрытие пула потоков (если вам это нужно), а также контролировать количество потоков в пуле во время выполнения.

person Martin James    schedule 15.06.2013