Swift异步打印顺序?

16

这段代码输出的顺序总是1 5 2 4 3吗?


Translated text:

这段代码输出的顺序总是1 5 2 4 3吗?

print("1")
DispatchQueue.main.async {
    print("2")
    DispatchQueue.main.async {
        print(3)
    }
    print("4")
}
print("5")

我觉得答案是否定的,但我无法解释清楚,希望有人能够澄清我的理解。谢谢!


1
是的,1和5很明显之后它切换到主队列并打印2。然后你再次请求主队列任务,但它不会等待它并打印4,然后是3。 - Prashant Tukadiya
3
请澄清一件事,即 print("1") 是否在主线程中执行。 - OOPer
1
为什么这很重要呢?它总是会首先执行。 - Unikorn
我怀疑这是由于执行顺序引起的: print("1") // 1. 执行打印1 DispatchQueue.main.async { // 2. 切换到主线程 print("2") // 4. 执行打印3 DispatchQueue.main.async { // 5. 切换到主线程 print(3) // 7. 执行打印3 } print("4") // 6. 执行打印4 } print("5") // 3. 执行打印5 - Unikorn
@OOPer,对不起,之前我错误地认为它会始终先执行,请原谅我的无知。 - Unikorn
1
@Unikorn,别担心,事实上我不擅长像Cristik那样写出好的图像。现在你有两个精彩的答案,你知道我为什么写这个评论了。 - OOPer
2个回答

16

这取决于你从哪个线程开始操作。

如果你从主线程开始,那么你会得到1, 5, 2, 4, 3

如果你从后台线程开始,大多数情况下你会得到相同的结果(1, 5, 2, 4, 3),但这并不保证,因为后台线程随时可以被操作系统挂起,如果这发生在print(5)调用之前,那么5将是最后一个被打印的。

请注意,如果问题中的代码是你应用程序/工作区中唯一的代码,那么你可能会惊讶地遇到部分打印输出,因为应用程序/工作区在命中print(5)行后立即退出,没有机会执行异步调度。为了规避这个问题,你可以确保RunLoop.current.run()在代码的最后部分在主线程上执行。

这里有一些示意图,试图阐述只使用主线程的情况和涉及后台线程的情况:

enter image description here enter image description here


11
您将始终获得1、2、4、3,5始终在1之后。但它相对于其他数字的位置取决于整个过程开始时所在的队列。
如果从主队列启动,则5始终位于1和2之间。
原因如下:
该代码从主队列开始运行。打印1。然后您将另一个块排队以异步方式运行在主队列上,因此该块将在当前块完成并且主队列到达当前运行循环的末尾后运行。代码继续到下一行,即打印5。当前块结束,然后运行主队列上的下一个块。这是第一次调用DispatchQueue.main.async的块。当此块运行时,它会打印2(现在我们有1 5 2)。像上一个一样,又将一个块排队到主队列。当前块继续并打印4(现在我们有1 5 2 4)。该块结束,然后运行主队列上的下一个块。这是我们添加的最后一个块。该块运行并打印3,给出最终输出:1 5 2 4 3。
如果从某个后台队列开始,则5可以出现在1之后的任何位置,但对于这么简单的代码,5很可能仍然出现在1和2之间,但在一般情况下,它可以出现在1之后的任何位置。
原因如下:
该代码从后台队列开始运行。打印1。然后您将另一个块排队以异步方式运行在主队列上,因此该块将在当前主队列运行循环完成后运行。代码继续到下一行,即打印5。当前块结束。添加到主队列的块与后台队列并行运行。取决于添加到主队列的块何时相对于后台队列上剩余代码运行,print("5")的输出可以与来自主队列的打印混合。这就是为什么从后台启动时,5可以出现在1之后的任何位置。
但是,问题中的简单代码可能始终提供1 5 2 4 3,即使在后台启动也是如此,因为代码非常短,所需时间非常少。
以下是将5放在其他地方的一些代码:
func asyncTest() {
    print("1")
    DispatchQueue.main.async {
        print("2")
        DispatchQueue.main.async {
            print(3)
        }
        print("4")
    }

    for _ in 0...1000 {
    }

    print("5")
}

DispatchQueue.global(qos: .background).async {
    asyncTest()
}
循环存在导致数字5在出现之前需要稍微等待一段时间,这使得主队列能够在输出5之前执行一些操作。如果没有循环,后台线程执行得太快,所以数字5会在2之前出现。
如果在playground中运行此测试,请添加:
PlaygroundPage.current.needsIndefiniteExecution = true

将其移至游乐场的顶部(导入之后)。您还需要:

import PlaygroundSupport

我在 Playground 中运行了 10 次,每次都得到以下结果:1、5、2、4、3。 - Unikorn
1
很可能你正在主队列上启动这个操作,因此它将始终给出相同的结果,就像我在我的答案中所述。 - rmaddy
1
@Unikorn,我在我的答案中添加了更多的解释。看一下,看看是否澄清了问题。 - rmaddy
1
我在游乐场上运行了几次示例代码。它仍然给我结果。5不在1和2之间的概率是多少? - Rakesha Shastri
1
每次我运行我的答案代码时,我在4和3之间得到5。也许你的电脑更快。尝试增加循环次数。 - rmaddy
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接