swift - 使用 GCD,DispatchGroup 如何在组离开之前(或之后)从后台队列在主队列上运行项目,而不会出现死锁?
问题描述
给定某个Foo
在后台线程上调用 Bar 以执行某些工作的类,如何Bar
在封闭函数返回它需要返回的值之前设置一些工作在主线程上完成而不会导致死锁?
例如,“asdf”如何在“done”打印之前打印,beforetrue
从func done() -> Bool
下面的示例中返回?
import Dispatch
class Foo {
/// always called from the main queue
func done() -> Bool {
let group = DispatchGroup()
group.enter()
DispatchQueue.global().async {
Bar().perform {
DispatchQueue.main.async { print("asdf") }
// "asdf" prints after "done" is printed
group.leave()
}
}
group.wait()
print("done")
return true
}
}
哪里Bar
很简单:
struct Bar {
func perform(_ work: @escaping () -> Void) { work() }
}
我需要 Bar 能够设置一些应该在done()
返回之前在主队列上执行的工作,而不会导致死锁(如果我们将上面的perform
块更改为使用DispatchQueue.main.sync
,并且(假设done()
总是在主线程上调用,这是)。
在我们打印“完成”之前,我能想出的打印“asdf”的唯一解决方案是更改done()
函数如下:
func done() -> Bool {
let group1 = DispatchGroup()
var completion: (() -> Void)? = nil
group1.enter()
DispatchQueue.global().async(group: group1) {
Bar().perform {
completion = { print("asdf") }
group1.leave()
}
}
group1.wait()
completion?()
print("done")
return true
}
这里的completion
块在函数返回之前在主线程上运行。然而,这感觉很笨拙和笨拙……似乎 GCD 应该为我们处理。但是我尝试过的所有其他操作都将在函数返回后运行。
想法?
解决方案
GCD 下的主队列是不可重入的,done
除非阻塞主线程,否则不能立即停止返回,也不能(因此)done
在第一个异步方法之后返回值。
因此,正如您在评论中被告知的那样,您尝试做的事情是不可能的。
您可以通过(例如)另一个完成处理程序从 Bar 完成处理程序内部回调主线程;但你不能像你试图做的那样“等待”。
例如:
struct Bar {
func perform(_ work: @escaping () -> Void) {
print("work")
work()
}
}
class Foo {
func done(_ f: @escaping (Bool) -> ()) {
DispatchQueue.global().async {
Bar().perform {
DispatchQueue.main.async {
// adjust the order of these two lines as desired
print("asdf")
f(true)
}
}
}
}
}
let f = Foo()
f.done { what in
print(what)
print("done")
}
输出:
work
asdf
true
done