在 GCD(大中央调度)中创建dispatch_queues来说,多少是太多了



有一篇关于在 Swift 中构建的轻量级通知系统的精彩文章,作者是 Mike Ash:(https://www.mikeash.com/pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html)。

基本思想是创建可以"侦听"的对象,即在状态发生变化时调用回调。 为了使其线程安全,创建的每个对象都保留自己的dispatch_queue。 dispatch_queue仅用于控制关键部分:

dispatch_sync(self.myQueue) {
    // modify critical state in self
}

而且它可能不会引起高度争议。 令我震惊的是,您创建的每个可以侦听的对象都会创建自己的调度队列,只是为了锁定几行代码。

一张海报表明,OS_SPINLOCK会更快、更便宜;也许吧,但它肯定会占用更少的空间。

如果我的程序创建数百或数千个(甚至数万个对象),我是否应该担心创建如此多的调度队列? 可能大多数人甚至都不会被倾听,但有些人可能会。

两个对象不相互阻塞,即有单独的锁,这当然是有道理的,通常我不会三思而后行,比如在每个对象中嵌入一个pthread_mutex,而是整个调度队列? 真的可以吗?

好吧,关于Grand Central Dispatch的文档对调度队列的内部工作原理和确切成本相当模糊,但它确实指出:

GCD 提供并管理 FIFO 队列,您的应用程序可以以块对象的形式向其提交任务。提交到调度队列的块在完全由系统管理的线程池上执行。

因此,

听起来队列只不过是通过线程池对块进行排队的接口,因此在空闲时对性能没有/影响最小。

概念文档还指出:

您可以根据需要创建任意数量的串行队列

这听起来肯定像是创建串行调度队列并使其闲置几乎是微不足道的成本。

此外,我决定测试在具有一些 Open GL 内容的应用程序上创建 10,000 个串行和并发调度队列,并没有发现性能受到任何影响,FPS 保持不变,它只使用了额外的 4MB RAM(单个队列为 ~400 字节)。

在使用OS_SPINLOCK而不是调度队列方面,Apple 在其关于迁移线程的文档中非常清楚地表明,GCD 比使用标准锁更有效(至少在有争议的情况下)。

将基于锁的代码替换为队列可以消除与锁相关的许多惩罚,还可以简化剩余的代码。您可以创建一个队列来序列化访问该资源的任务,而不是使用锁来保护共享资源。队列不会施加与锁相同的惩罚。例如,将任务排队不需要陷阱到内核中即可获取互斥锁。

尽管还值得注意的是,如果您不使用它,您可以随时释放队列,如果您担心内存,则稍后需要再次使用时重新创建它。


博士

调度队列是要走的路。您无需太担心创建大量队列而不使用它们,而且它们肯定比锁更有效。

编辑:您实际上发现旋转锁在无争议的情况下更快,因此您可能希望将其用于此!

相关内容

最新更新