信号量

Swift 并发的同步原语

要求: iOS 13.0+ / macOS 10.15+ / tvOS 13.0+ / watchOS 6.0+ • Swift 5.10+ / Xcode 15.3+

📖 文档


此包提供了 AsyncSemaphore,一个传统的计数信号量

DispatchSemaphore不同,它不会阻塞任何线程。相反,Swift 并发任务会被挂起, "等待" 信号量。

用法

您可以使用信号量来挂起一个任务,并在稍后恢复它

let semaphore = AsyncSemaphore(value: 0)

Task {
  // Suspends the task until a signal occurs.
  await semaphore.wait()
  await doSomething()
}

// Resumes the suspended task.
semaphore.signal()

一个 Actor 可以使用信号量来防止其方法并发运行,从而避免 "Actor 重入问题"

actor MyActor {
  private let semaphore = AsyncSemaphore(value: 1)
  
  func serializedMethod() async {
    // Makes sure no two tasks can execute
    // serializedMethod() concurrently. 
    await semaphore.wait()
    defer { semaphore.signal() }
    
    await doSomething()
    await doSomethingElse()
  }
}

信号量通常可以限制对资源的并发访问数量

class Downloader {
  private let semaphore: AsyncSemaphore

  /// Creates a Downloader that can run at most
  /// `maxDownloadCount` concurrent downloads. 
  init(maxDownloadCount: Int) {
    semaphore = AsyncSemaphore(value: maxDownloadCount) 
  }

  func download(...) async throws -> Data {
    try await semaphore.waitUnlessCancelled()
    defer { semaphore.signal() }
    return try await ...
  }
}

您可以在最新的示例中看到,wait() 方法有一个 waitUnlessCancelled 变体,如果任务在信号发生之前被取消,则会抛出 CancellationError

有关信号量的精彩介绍,请参阅Swift 中信号量的美妙之处 🚦。这篇文章讨论了 DispatchSemaphore,但它可以很容易地移植到 Swift 并发:从上面的例子中获得灵感。