【发布时间】:2020-01-16 15:51:10
【问题描述】:
所以我一直在使用 NetworkExtension 来实现玩具 VPN,但我遇到了完成处理程序/异步运行代码的问题。我将引导您完成我的思路/尝试,并感谢您在我犯错的领域以及如何解决此问题的任何指示!
这是最小的可重现代码(显然你需要import NetworkExtension):
let semaphore = DispatchSemaphore(value: 0)
NETunnelProviderManager.loadAllFromPreferences { managers, error in
print("2 during")
semaphore.signal()
}
print("1 before")
semaphore.wait()
print("3 after")
根据我对信号量和异步代码的理解,我希望打印输出按顺序发生:
1 before
2 during
3 after
但是程序在“1 before”处挂起。如果我删除 semaphore.wait() 行,打印输出按预期的顺序发生:1,3,2(因为稍后运行闭包)。
因此,在使用调试器进行了一些研究之后,看起来信号量陷阱循环正在阻塞执行。这促使我阅读了一些队列,我发现将其更改为以下工作:
// ... as before
DispatchQueue.global().async {
semaphore.wait()
print("3 after")
}
这是有道理的,因为阻塞的.wait() 调用现在在单独的线程中被异步调用。但是,我并不需要这种解决方案,因为在我的实际实现中,我实际上是从闭包中捕获结果并稍后返回它们,如下所示:
let semaphore = DispatchSemaphore(value: 0)
var results: [NETunnelProviderManager]? = nil
NETunnelProviderManager.loadAllFromPreferences { managers, error in
print("2 during")
results = managers
semaphore.signal()
}
print("1 before")
// DispatchQueue.global().async {
semaphore.wait()
print("3 after")
// }
return results
显然我无法从 async 闭包返回数据,并且将返回移出它会使其失效。此外,添加另一个信号量以使事情同步会出现与之前在链中移动问题相同的问题。
因此,我决定尝试将 .loadAllFromPreferences() 调用和完成处理程序放在 async 闭包中,并将其他所有内容保留在原始代码 sn-p 中:
// ...
DispatchQueue.global().async {
NETunnelProviderManager.loadAllFromPreferences { loadedManagers, error in
print("2 during")
semaphore.signal()
}
}
// ...
但是这不起作用,.wait() 调用从未通过 - 和以前一样。我假设信号量仍然以某种方式阻塞线程并且不允许执行任何操作,这意味着系统中管理队列的任何内容都没有运行异步块?但是,我在这里抓着稻草,担心我最初的结论可能不正确。
这是我开始超出我的深度的地方,所以我想知道实际发生了什么,您建议采用什么分辨率以同步方式从.loadAllFromPreferences() 获取结果?
谢谢!
【问题讨论】:
-
异步等待调度信号量没有意义。信号量的全部意义在于暂停并等待某事完成。这样做会异步删除暂停,并随之删除信号量的整个点。
标签: swift asynchronous closures grand-central-dispatch dispatchsemaphore