Swift并发:' async let '模拟器上的奇怪行为



我运行以下代码片段,并期望loadFromNetworkloadFromDB并行运行。

class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()

Task {
async let networkData = loadFromNetwork()
async let dbData = loadFromDB()
await print("(networkData) (dbData)")
}
}
}
func loadFromNetwork() async -> String {
print("loadFromNetwork started (Thread.current)")
// (0...10000000).forEach { String($0) }
sleep(3)
print("loadFromNetwork finished")
return "Network Data"
}
func loadFromDB() async -> String {
print("loadFromDB started (Thread.current)")
// (0...10000000).forEach { String($0) }
sleep(1)
print("loadFromDB finished")
return "DB Data"
}

但是我看到loadFromNetworkloadFromDB在同一个后台线程上依次运行。为什么会这样呢?

loadFromNetwork started <NSThread: 0x6000008d7180>{number = 3, name = (null)}
loadFromNetwork finished
loadFromDB started <NSThread: 0x6000008d7180>{number = 3, name = (null)}
loadFromDB finished
Network Data DB Data

我发现这种情况只发生在模拟器上。我在iPhone 13 Pro Max和iPhone 12 Prod Max上进行了测试。在我的iPhone 7设备上,这一切都运行良好。我已经更新了标题。

loadFromNetwork started <NSThread: 0x2834cc080>{number = 3, name = (null)}
loadFromDB started <NSThread: 0x2834c2280>{number = 5, name = (null)}
loadFromDB finished
loadFromNetwork finished
Network Data DB Data

你的测试有缺陷。一方面,不要将线程和线程睡眠与结构化并发性混合使用。sleep的使用阻塞了。结构化并发await的重点在于它不会阻塞。

下面是一个可以正确测试的重写:

extension Task where Success == Never, Failure == Never {
static func sleep(_ seconds:Double) async {
await self.sleep(UInt64(seconds * 1_000_000_000))
}
static func sleepThrowing(_ seconds:Double) async throws {
try await self.sleep(nanoseconds: UInt64(seconds * 1_000_000_000))
}
}

class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
Task {
async let networkData = loadFromNetwork()
async let dbData = loadFromDB()
await print("(networkData) (dbData)")
}
}
}
func loadFromNetwork() async -> String {
print("loadFromNetwork started (Thread.current)")
await Task.sleep(3.0)
print("loadFromNetwork finished")
return "Network Data"
}
func loadFromDB() async -> String {
await Task.sleep(1.0)
print("loadFromDB started (Thread.current)")
await Task.sleep(1.0)
print("loadFromDB finished")
return "DB Data"
}
我的输出是:
loadFromNetwork started <NSThread: 0x60000198bb40>{number = 5, name = (null)}
loadFromDB started <NSThread: 0x600001999500>{number = 7, name = (null)}
loadFromDB finished
loadFromNetwork finished
Network Data DB Data

正如您所看到的,子任务是交错的(并发的),正如您所希望的那样。

即便如此,看线程号仍然是错误的。系统完全可以为这些子任务的所有不同部分使用相同的线程。例如,如果我从loadFromDB中删除第一个睡眠,我得到:

loadFromNetwork started <NSThread: 0x600002bf2480>{number = 7, name = (null)}
loadFromDB started <NSThread: 0x600002bf2480>{number = 7, name = (null)}
loadFromDB finished
loadFromNetwork finished
Network Data DB Data

所以至少在开始时他们使用同一个线程-为什么不呢?我们仍然得到并发,这才是最重要的。你不应该偷看我们在哪个线程上。

(我有时调用Thread.isMainThread只是为了确保上下文是如何切换的,但即使这样也有可能产生Heisenbug。)

最新更新