Apple doc的GCD生产者-消费者解决方案错误?
在 Apple 并发编程指南的“从线程迁移”部分中,有
Changing Producer-Consumer Implements,声称可以简化典型的多步pthread互斥+条件变量实现使用 GCD。
使用调度队列,您可以简化生产者和消费者 实现到单个调用中:
dispatch_async(queue, ^{
// Process a work item.
});
当你的生产者有工作要做时,它所要做的就是将该工作添加到队列中并让队列处理该项目。
生产者-消费者问题也称为有界缓冲区问题,但上面没有提到缓冲区、其边界或消费者,更不用说阻塞生产者和消费者了。消费者以避免超载/欠载。
这怎么可能是一个有效的解决方案呢?
In the Migrating Away from Threads section of Apple's Concurrency Programming Guide, there is
Changing Producer-Consumer Implementations, which claims that the typical multistep pthread mutex + condition variable implementation can be simplified using GCD.
With dispatch queues, you can simplify the producer and consumer
implementations into a single call:
dispatch_async(queue, ^{
// Process a work item.
});
When your producer has work to be done, all it has to do is add that work to a queue and let the queue process the item.
The Producer-Consumer problem is also known as the Bounded-Buffer problem, yet the above makes no mention of a buffer, its bound or the consumer, let alone blocking the producer & consumer in order to avoid over/under runs.
How can this be a valid solution?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
在该Apple文档中描述的解决方案中:
假设您有多个生产者和消费者,生产者将数据放置在共享缓冲区中,消费者从该共享缓冲区读取数据。信号量或监视器用于同步对共享缓冲区的访问,并且缓冲区大小是固定的,以便根据消耗的速率限制正在生成的数据量,从而限制生产者。
在 Grand Central Dispatch 下,消费者是调度到队列的任务。由于任务是 Objective-C 块,因此生产者不需要缓冲区来告诉消费者它应该处理的数据:Objective-C 块自动捕获它们引用的对象。
例如:
生产者可以放置与其生产的数据一样多的消费者任务。然而,这并不意味着 GCD 将以相同的速率触发消费者任务。 GCD使用操作系统信息来根据当前系统负载来控制执行的任务量。生产者本身不会受到限制,并且在大多数情况下,由于 GCD 固有的负载平衡,因此不必受到限制。
如果确实需要限制生产者,一种解决方案是让一个 master 分派 n 个生产者任务,并让每个消费者通知 master(通过消费者完成其工作后分派的任务)它已经结束,在这种情况下,master 将调度另一个生产者任务。或者,消费者本身可以在完成后调度生产者任务。
具体回答您所解决的问题:
不需要共享缓冲区,因为消费者是 Objective-C 块,它们会自动捕获它们引用的数据。
GCD根据当前系统负载来限制调度任务的数量。
消费者是指派发到 GCD 队列的任务。
由于没有共享缓冲区,因此不需要阻塞。由于每个消费者都是一个 Objective-C 块,通过 Objective-C 块上下文捕获机制捕获生成的数据,因此消费者和数据之间存在一对一的关系。
In the solution decribed at that Apple document:
Say you have multiple producers and consumers, producers place data in a shared buffer and consumers read data from that shared buffer. A semaphore or monitor is used to synchronise access to the shared buffer, and the buffer size is fixed so as to limit the amount of data that are being produced according to the rate they’re being consumed, hence throttling the producer.
Under Grand Central Dispatch, consumers are tasks dispatched to a queue. Since tasks are Objective-C blocks, a producer doesn’t need a buffer to tell a consumer about the data it should process: Objective-C blocks automatically capture objects they reference.
For example:
The producer may place as many consumer tasks as data it can produce. However, this doesn’t mean that GCD will fire the consumer tasks at the same rate. GCD uses operating system information to control the amount of tasks that are executed according to the current system load. The producer itself isn’t throttled, and in most cases it doesn’t have to be because of GCD’s intrinsic load balancing.
If there’s actual need to throttle the producer, one solution is to have a master that would dispatch n producer tasks and have each consumer notify the master (via a task that’s dispatched after the consumer has finished its job) that it has ended, in which case the master would dispatch another producer task. Alternatively, the consumer itself could dispatch a producer task upon completion.
Specifically answering the items you’ve addressed:
A shared buffer isn’t needed because consumers are Objective-C blocks, which automatically capture data that they reference.
GCD bounds the number of dispatched tasks according to the current system load.
Consumers are the tasks dispatched to GCD queues.
There’s no need for blocking since there’s no shared buffer. As each consumer is an Objective-C block capturing the produced data via the Objective-C block context capturing mechanism, there’s a one-to-one relation between consumer and data.