ios - 是 API 将自身分派(dispatch)到队列并调用回调更好,还是 API 调用者进行分派(dispatch)更好?

标签 ios cocoa grand-central-dispatch

例子:

具有自己调度的异步方法:

// Library
func asyncAPI(callback: Result -> Void) {
  dispatch_async(self.queue) {
    ...
    callback(result)
  }
}

// Caller
asyncAPI() { result in
  ...
}

暴露调度队列的同步方法:

// Library
func syncAPI() -> Result {
  assert(isRunningOnCorrectQueue())
  ...
  return result
}

// Caller
dispatch_async(api.queue) {
  let result = api.syncAPI()
  ...
}

这两个示例的行为相同,但我希望了解其中一个示例是否最终使大型代码库比另一个更复杂,尤其是在存在大量异步时。

最佳答案

我会反对您提出的两种模式。

对于第一种模式(API 管理它自己的后台),我认为这样做的好处很少或没有好处,而不是将其留给调用者。如果您想使用私有(private)的串行队列来保护 API 内部的数据(或任何其他类型的关键部分),那很好,但是该队列应该是私有(private)的,并且它不应该专门针对任何公共(public)的、非全局的-并发队列(注意:它应该特别是不针对主队列)。理想情况下,您的 API 的主要实现也将采用第二个参数,因此调用者可以指定在哪个队列上调用回调。 (人们可以通过传递一个重新分派(dispatch)到他们想要的队列的回调 block 来解决缺少这样一个参数的问题,但我认为这比有一个额外的可选参数更笨重。)这使 API 消费者可以完全控制并发性,同时保留您在内部使用队列来保护状态的自由。

至于第二种方法,我认为我们都应该避免创建新的同步、阻塞 API。当您提供一个同步的、阻塞的 API 而没有提供一个基于回调的版本时,这意味着您已经拒绝了 API 的消费者任何避免阻塞的机会。当你只提供同步、阻塞的 API 时,如果有人想在后台调用你的 API,至少有一个线程(除了你的 API 在幕后消耗的任何额外线程之外)将从有限数量的可用线程中消耗到每个过程。 (在最坏的情况下,这可能导致饥饿条件,这实际上是死锁。)

第二个例子的另一个危险信号是它售卖队列;每当 API 发送 队列时,就会出现问题。如前所述,如果您想使用私有(private)串行队列来保护 API 内部的状态或其他关键部分,那就去做吧,但不要将该队列暴露给外界。如果不出意外,它会不必要地公开您的实现细节。在查看系统框架 header 时,我找不到一个案例,其中一个 dispatch_queue_t 被出售的地方不是很明显 API 消费者的意图是推送 in 队列,而不是将其读出

还值得一提的是,无论您的工作负载是 CPU 密集型还是 IO 密集型,这些模式都是有问题的。如果它受 CPU 限制,那么不管理您自己的分派(dispatch)可以让 API 的使用者明确控制此 CPU 工作的执行方式。如果您的工作负载是 IO 绑定(bind)的,那么您应该使用操作系统和 libdispatch 提供的异步 IO 机制(dispatch_iodispatch_sourceskevent、等)以避免在工作期间消耗一个(或多个)线程。

这里的另一个答案暗示强制消费者管理他们自己的并发会导致“样板”代码。如果您觉得 API 消费者可能不得不使用 dispatch_async 包装对您的 API 的调用的负担太大,那么可以随意提供一个方便的重载来分派(dispatch)到默认的全局并发队列,但请始终保留允许 API 使用者显式管理自己的并发性的版本。

另一方面,如果所有这些都是实现内部的,而不是公共(public) API 的一部分,那么做任何最方便的事情,因为你知道你可以在未来的任何时候重构公共(public) API 背后的实现。

关于ios - 是 API 将自身分派(dispatch)到队列并调用回调更好,还是 API 调用者进行分派(dispatch)更好?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25945922/

相关文章:

ios - GCD NSURLSession completionHandler block 返回值 null

ios - swift 中的 dispatch_block_t 相当于什么?

ios - 与两个透明纹理的 Alpha 混合无法正常工作

iOS - 从 CBPeripheral 对象获取外设的 MAC 地址

objective-c - 与存储在 NSMutableArray 中的 UIView 进行交互

xcode - 带圆角的 NSVisualEffectView

ios - CallKit 错误 com.apple.CallKit.error.requesttransaction 错误 2

ios - 如何通过添加小时来更改 NSDate 的日期?

objective-c - 全屏怎么挡热角

concurrency - 使用 dispatch_async 的 EXC_BAD_ACCESS