好吧,如果你被限制只使用信号量,你可以使用两个信号量来创建一个无界的生产者-消费者队列类,你可以用它来实现一个线程池。
您需要一个用于任务对象的“SimpleQueue”类。我假设您已经拥有一个,可以轻松构建一个或其他任何东西。
在您的“ProducerConsumerQueue”类的 ctor 中(或在 main() 中,或在返回 *ProducerConsumerQueue 结构的某些工厂函数中,无论您的语言有什么),创建一个 SimpleClass 和两个信号量。一个“QueueCount”信号量,初始化为 0,一个“QueueAccess”信号量,初始化为 1。
将 'push(*task)' 和 '*task pop()' 方法/memberFunctions/methods 添加到 ProducerConsumerQueue:
在 'push' 中,首先调用 QueueAccess 上的 'WaitForSingleObject()' API,然后将 *task 推送到 SimpleQueue,然后在 QueueAccess 上调用 ReleaseSemaphore() API。这会以线程安全的方式推送 *task。然后 QueueCount 上的 ReleaseSemaphore() - 这将发出任何等待线程的信号。
在 pop() 中,首先在 QueueCount 上调用 'WaitForSingleObject()' API - 这确保任何调用消费者线程都必须等待,直到队列中有一个 *task。然后在 QueueAccess 上调用 'WaitForSingleObject()' API,然后从 SimpleQueue 中弹出任务,然后在 QueueAccess 上调用 ReleaseSemaphore() API 并返回任务- 这个线程安全地将 *task 出列。
创建 ProducerConsumerQueue 后,创建一些线程来运行任务。在 CreateThread() 中,传递相同的 *ProducerConsumerQueue 作为 'auxiliary' *void 参数。
在线程函数中,将 *void 转换回 *ProducerConsumerQueue,然后永远循环,调用 pop(),然后运行返回的任务。
好的,你的线程池现在已经准备好做一些事情了。如果要运行 20 个任务,请在循环中创建它们并将它们推送到 ProducerConsumerQueue。然后线程将全部运行它们。
您可以在池中创建任意数量的线程(在合理范围内)。对于 CPU 密集型任务,与内核一样多的线程是合理的。如果任务进行阻塞调用,您可能希望创建更多线程以获得最快的整体吞吐量。
一个有用的增强是在接收到每个任务后检查线程函数循环中的“null”,如果它为null,则清理退出线程,从而终止它。这允许通过排队空值轻松终止线程,从而更容易关闭线程池(如果需要),并且还可以在运行时控制池中的线程数。