Хорошо, если вы ограничены использованием только семафоров, вы можете использовать два семафора для создания неограниченного класса очереди производитель-потребитель, который вы могли бы использовать для реализации пула потоков.
Вам нужен класс 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