并发调度队列:

func myConcurrentQueue() {
    let concurrentQueue = DispatchQueue(label: "concurrent", attributes: .concurrent)
    concurrentQueue.async {
        print("Task 1 Con Start")
        print("Task 1 Con Finished")
    }
    concurrentQueue.async {
        print("Task 2 Con Start")
        print("Task 2 Con Finished")
    }
}

myConcurrentQueue()

我想结果应该是:

Task 1 Con Start
Task 2 Con Start
Task 1 Con Finished
Task 2 Con Finished

但结果如下图所示

enter image description here

因此,我从上面的问题中遇到了两个问题.

  1. 为什么并发队列返回这样的结果?
  2. DispatchQueue中的label是否会影响结果?

推荐答案

分派的工作不会花费足够长的时间来体验并行执行.第一个可能跑得太快,以至于在第二个有机会开始之前就结束了.(print输出是同步的,这一事实加剧了这一点.)try 插入Thread.sleep(…)(您在生产代码中永远不会这样做,但对于诊断目的很有用)或一些耗时的事情.

例如:

func myConcurrentQueue() {
    let concurrentQueue = DispatchQueue(label: "concurrent", attributes: .concurrent)
    let start = ContinuousClock().now
    concurrentQueue.async {
        print("Task 1 Con Start at", start.duration(to: .now))
        Thread.sleep(forTimeInterval: 1)                           // never do this in production app; just for diagnostic purposes
        print("Task 1 Con Finished at", start.duration(to: .now))
    }
    concurrentQueue.async {
        print("Task 2 Con Start at", start.duration(to: .now))
        Thread.sleep(forTimeInterval: 1)                           // never do this in production app; just for diagnostic purposes
        print("Task 2 Con Finished at", start.duration(to: .now))
    }
}

我可能建议包含一些信息,以显示有关何时生成输出的信息(因此我在上面使用了ContinuousClock).

无论如何,这会产生类似的结果:

Task 1 Con Start at 0.001328375 seconds
Task 2 Con Start at 0.001392083 seconds
Task 2 Con Finished at 1.006529791 seconds
Task 1 Con Finished at 1.006475916 seconds

注意,因为它们是并行运行的,所以从技术上讲,您无法确定哪一项最先到达其各自的print条语句(因此,在本例中,任务2恰好比任务1早完成零点几毫秒),但您可以看到它们并行运行.

而且,在回答你的问题时,不,标签的 Select 并不重要.仅用于诊断目的.


另外,请注意,仅仅因为您使用并发队列并不能保证您一定会经历并行执行.这取决于其他核心是否在忙于做其他事情.此外,我对在操场上测试这类东西持谨慎态度(如果你是这样的话),因为这有时会表现出与真实应用程序相当不典型的执行特征.


可视化并行执行的另一种方法是使用工具中的"兴趣点"工具,如How to identify key events in Xcode Instruments?中所述

import os.signpost

let poi = OSSignposter(subsystem: "Test", category: .pointsOfInterest)

func myConcurrentQueue() {
    let concurrentQueue = DispatchQueue(label: "concurrent", attributes: .concurrent)
    concurrentQueue.async {
        poi.withIntervalSignpost("1") {
            Thread.sleep(forTimeInterval: 1)                       // never do this in production app; just for diagnostic purposes
        }
    }
    concurrentQueue.async {
        poi.withIntervalSignpost("2") {
            Thread.sleep(forTimeInterval: 1)
        }
    }
}

这使我们可以从Instruments中可视化并发队列在时间线上的行为,并且您可以看到它们并行运行.但同样,我们之所以看到这一点,是因为我让它做了一些足够慢的操作,使得第一个工作项很可能在第二个工作项有机会启动之前无法完成:

enter image description here


请注意,现在我们通常不会使用GCD并发队列.我们将改用SWIFT并发性,在本例中为"task group":

func myTaskGroup() async {
    await withTaskGroup(of: Void.self) { group in
        group.addTask {   // first task
            …
        }
        group.addTask {   // second task
            …
        }
    }
}

有关更多信息,请参阅WWDC 2021视频12:5232/" rel="nofollow noreferrer">Meet async/await in Swift.并在Explore structured concurrency in Swift中的12:52处引入任务组.

归根结底,SWIFT并发性超出了问题的范围,但我想分享并发调度队列的现代替代方案.

Swift相关问答推荐

为什么我的引用类型对象在初始化后有3个强引用?

为什么第二项任务不在第一项任务完成之前开始?(并发队列)

按Esc键时执行操作(工作表)

如何在枚举有关联数据时使用combined if with case

从SwiftUI使用UIPageView时,同一页面连续显示两次的问题

文件命名&NumberForMatter+扩展名&:含义?

如何在 Vapor 中制作可选的查询过滤器

SwiftUI 组合问题:通过 AppEventsManager 在 ViewModel 之间共享数据

如何从UIKit中的AttributeContainer获取属性并将其分配给某个变量

如何使用Swift宏和@Observable和@Environment?

设备上ScrollView为什么会将内容高度更改为随机值,而在预览上不会? - 优化后的标题:设备上ScrollView内容高度随机变化问题解决

SwiftUI:列表背景为空列表时为黑色

从 Swift Vapor Server 中调用 ShazamKit

如何在 Combine 合并运算符的输出上使用 eraseToAnyPublisher

为什么 Apple Scrumdinger Sample App 使用两个事实来源?

SwiftUI:当先前的视图列表更改时,NavigationView 详细视图会弹出回栈

RxSwift:share() 替代方案,保证upstream 的单一订阅

如何在 Xcode 中的 UITests 下以通用方式访问后退栏按钮项?

在 Swift 中强制崩溃的最简单方法

Swift UITableView reloadData 在一个闭包中