【发布时间】:2017-12-10 13:46:15
【问题描述】:
所以,我花了一些时间尝试让 DispatchGroup 在长时间的异步操作完成之前阻止 for 循环进行迭代。我发现的大多数示例都相当简单明了,但我似乎无法让我的简单测试用例按预期工作。
let group = DispatchGroup()
for i in 1...3 {
group.enter()
print("INDEX \(i)")
asynchronousOperation(index: i, completion: {
print("HELLO \(i)")
self.group.leave()
})
print("OUTSIDE \(i)")
}
func asynchronousOperation(index: Int, completion: @escaping () -> ()) {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+5) {
print("DONE \(index)")
completion()
}
}
这最终会打印出来
START
INDEX 1
OUTSIDE 1
INDEX 2
OUTSIDE 2
INDEX 3
OUTSIDE 3
DONE 1
HELLO 1
DONE 2
HELLO 2
DONE 3
HELLO 3
我希望它能打印出更像
的东西START
INDEX 1
OUTSIDE 1
HELLO 1
INDEX 2
OUTSIDE 2
HELLO 2
INDEX 3
OUTSIDE 3
HELLO 3
只要在 asynchronousOperation() 内部调用 group.leave() 之前,不会打印 OUTSIDE 之后的下一个“INDEX”
我可能误解了一些简单的事情——有什么想法吗?
【问题讨论】:
-
您没有使用调度组。使用
group.wait或group.notify,该组无用。无论如何,组并不是你真正需要的。 -
为了未来的读者,如果不承认这是一种反模式,就不能完整地讨论这个主题,除非非常特殊的情况,否则应该避免,因为如果使用不正确(而且几乎总是是),它可能会导致各种问题,无论是微妙的还是严重的。这是一种令人陶醉的模式,因为它似乎简化了一个人的网络代码,用诱人的简单和同步模式代替了复杂的异步模式。但这几乎总是错误的解决方案。
-
@Rob 你能描述一下在这种情况下首选的模式是什么吗?
-
Kumar 和 Andrea 向您展示了如何更改代码以实现“预期”输出,但它们涉及阻塞当前线程,这通常是一个坏主意(如果当前线程是主线程)。优选的模式包括(a)让它异步运行(就像你的第一个输出一样)并根据需要组织结果;或者 (b) 如果您绝对必须让一个异步任务在前一个任务完成之前不启动,请创建自定义异步
Operation子类,并在它们之间建立依赖关系。 -
简而言之,这个问题有点抽象,生成的答案达到了你的预期输出,但在实践中通常是错误的。如果问题是“为什么我得到了我所做的输出”(如果你仍然不清楚这个问题),我很乐意发布一个答案。如果您的问题是“我知道为什么我得到了我所做的输出,但想知道我需要做什么才能实现所需的输出”,那么我会反击并询问
asynchronousOperation正在做什么的真实示例,并且为什么要让当前线程等待它。正确的解决方案将取决于更广泛的问题是什么。
标签: swift asynchronous grand-central-dispatch