0

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

串行队列(也称为私有调度队列)按添加到队列的顺序一次执行一项任务。当前正在执行的任务在由调度队列管理的不同线程上运行(可能因任务而异)。串行队列通常用于同步对特定资源的访问。

但是我们可以很容易地制造死锁如何在 Grand Central Dispatch 中制造死锁?通过执行sync内部async

let serialQueue = DispatchQueue(label: "Cache.Storage.SerialQueue")
serialQueue.async {
  serialQueue.sync {
    print("perform some job")
  }

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

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

我在fs 模块中看到它同时支持syncasync功能,例如fs.writeFileSync(file, data[, options])fs.writeFile(file, data[, options], callback)。通过允许两个版本,这意味着用户可以按照他们想要的任何顺序使用它们?所以他们可以像我们上面所做的那样轻松创建死锁?

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

4

1 回答 1

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

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

避免这种僵局的方法是永远不要那样做。当您考虑可以通过以下方式达到相同的效果时,这尤其愚蠢:

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

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

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

但是,永远没有理由在同一个队列上同步做某事,你不妨只是做某事。或者换一种说法,如果你只是一个接一个地编写语句,它们已经在同一个队列上同步执行。

我在 fs 模块中看到它同时支持同步和异步功能,例如 fs.writeFileSync(file, data[, options]) 和 fs.writeFile(file, data[, options], callback)。通过允许两个版本,这意味着用户可以按照他们想要的任何顺序使用它们?所以他们可以像我们上面所做的那样轻松创建死锁?

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

于 2017-09-19T14:08:09.800 回答