1

我运行以下代码片段并期望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
4

1 回答 1

1

你的测试有缺陷。一方面,不要将线程和线程睡眠与结构化并发混合在一起。您对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。)

于 2021-10-12T13:04:53.900 回答