同步和异步如何与线程安全一起工作?

How both sync and async work with thread safe?

在 Swift 中,我们可以利用 DispatchQueue 来防止竞争条件。通过使用串行队列,所有事情都按顺序执行,从https://developer.apple.com/library/content/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html

Serial queues (also known as private dispatch queues) execute one task at a time in the order in which they are added to the queue. The currently executing task runs on a distinct thread (which can vary from task to task) that is managed by the dispatch queue. Serial queues are often used to synchronize access to a specific resource.

但我们可以通过在 async

中执行 sync 轻松创建死锁 How do I create a deadlock in Grand Central Dispatch?
let serialQueue = DispatchQueue(label: "Cache.Storage.SerialQueue")
serialQueue.async {
  serialQueue.sync {
    print("perform some job")
  }

  print("this can't be reached")
}

防止死锁的唯一方法是使用 2 个串行队列,每个队列用于 syncasync 函数版本。但是当 writeSyncwriteAsync 同时发生时,这可能会导致罕见的情况。

我在 fs module 中看到它同时支持 syncasync 函数,例如 fs.writeFileSync(file, data[, options])fs.writeFile(file, data[, options], callback)。通过允许这两个版本,是否意味着用户可以按照他们想要的任何顺序使用它们?所以他们很容易像我们上面那样制造死锁?

所以也许 fs 有一个聪明的方法可以应用到 Swift?我们如何以线程安全的方式同时支持 syncasync

serialQueue.async {
    serialQueue.sync {
        print("perform some job")
    }
}

之所以会死锁,是因为这段代码将第二个任务排入同一个调度队列,然后等待第二个任务完成。然而,第二个任务甚至无法启动,因为它是一个串行队列,而第一个任务仍在执行(尽管被内部信号量阻塞)。

避免这种死锁的方法是永远不要那样做。当您认为可以通过以下方式实现相同的效果时,这尤其愚蠢:

serialQueue.async {
    print("perform some job")
}

有一些 运行 同步任务用例在与您所在队列不同的队列中,例如

  • 如果另一个队列是主队列,并且您想在 UI 中做一些事情,然后再继续
  • 作为不同队列中任务之间同步的一种方式,例如,如果你想确保另一个队列中的所有当前任务在继续之前都已完成。

然而,没有理由在同一个队列上同步做某事,你还不如做某事。或者换句话说,如果你只是一个接一个地写语句,它们已经在同一个队列上同步执行了。

I see in fs module that it supports both sync and async functions, like fs.writeFileSync(file, data[, options]) and fs.writeFile(file, data[, options], callback). By allowing both 2 versions, it means users can use them in any order they want? So they can easily create deadlock like what we did above?

这取决于两个 API 的实现方式。调用的同步版本可能只是执行调用而不会影响其他线程。如果它确实抓住了另一个线程,然后等待直到另一个线程完成,那么是的,如果 node.js 服务器用完线程,则可能会出现死锁。