添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

1. 概述

作为一名从OC过来的iOS程序员,对于OC的GCD肯定不会陌生,在项目中也会经常用到,一套漂亮的C函数,用起来得心应手,而到了swift,GCD变得更加面向对象,虽然原理一样,但是API却有较大变化。
下面就跟随笔者来看一下吧。

2. GCD常用类与属性

类或属性 描述
class DispatchQueue 一个可以在应用程序的主线程或后台线程上连续或并发地管理任务的执行的对象。
class DispatchWorkItem 要执行的任务,允许进行完成处理和执行依赖的封装。
class DispatchGroup 一个可以单独监测的任务组。
struct DispatchQoS 应用于任务的服务质量或执行优先级。
class DispatchSource 协调处理特定低级系统事件(如文件系统事件、定时器和UNIX信号)的对象。
class DispatchIO 使用基于流或随机访问语义管理文件描述符上的操作的对象。
struct DispatchData 管理基于内存的数据缓冲区并将其公开为连续内存块的对象。
struct DispatchDataIterator 在分派数据对象的内容上的逐字节迭代器。
class DispatchSemaphore 一个通过使用传统的计数信号量来控制跨多个执行上下文对资源的访问的对象。
struct DispatchTime 相对于默认时钟的时间点,精度为纳秒。
struct DispatchWallTime 根据挂钟显示的绝对时间点,具有微秒精度。
enum DispatchTimeInterval 关于时间的枚举值。
enum DispatchTimeoutResult 一个指示被分发的任务是否在制定的时间完成的结果值。
typealias dispatch_time_t typealias dispatch_time_t = UInt64
var DISPATCH_WALLTIME_NOW: UInt 当前时间
class DispatchObject 大多数dispatch类型的基类。
enum DispatchPredicate 在给定执行上下文中计算的逻辑条件。
func dispatchPrecondition(condition() -> DispatchPredicate) 检查进一步执行所必需的分派条件。

以上的类、属性或者方法中,常用的也就是上面那几个,下面就逐个了解一下。

3. DispatchWorkItem

DispatchWorkItem,即派发工作项,可以将要执行的任务以闭包的方式封装到DispatchWorkItem对象中。
常用方法如下:

方法名称 描述
init(qos: DispatchQoS, flags: DispatchWorkItemFlags, block: () -> Void) 初始化方法,可直接带一个闭包,也可添加一些配置项再带闭包。
func perform() 在当前线程同步执行工作项任务。
func cancel() 取消工作项,异步。
func notify(queue: DispatchQueue, execute: DispatchWorkItem) 工作项任务结束后通知执行指定的工作项。
func notify(qos: DispatchQoS, flags: DispatchWorkItemFlags, queue: DispatchQueue, execute: () -> Void) 工作项任务结束后通知执行具有指定配置的闭包任务。
func wait() 阻塞当前线程,直到工作项任务结束。

代码示例如下:

func workItemDemo() {
        // 只带一个任务闭包
        let workItem1 = DispatchWorkItem {
            print("workItem 1,  thread: \(Thread.current)")
            sleep(1)
        // 除了任务闭包,还配置了qos(执行优先级)或flags(特殊行为标记)
        let workItem2 = DispatchWorkItem(qos: .unspecified, flags: .inheritQoS) {
            print("workItem 2,  thread: \(Thread.current)")
            sleep(1)
        workItem2.notify(qos: .unspecified, flags: .inheritQoS, queue: DispatchQueue.global()) {
            print("workItem 2 finished,  thread: \(Thread.current)")
        let workItem3 = DispatchWorkItem(qos: .unspecified, flags: .inheritQoS) {
            print("workItem 3,  thread: \(Thread.current)")
            sleep(1)
        workItem3.cancel()
        let workItem4 = DispatchWorkItem(qos: .unspecified, flags: .inheritQoS) {
            print("workItem 4,  thread: \(Thread.current)")
            sleep(1)
        workItem4.notify(queue: DispatchQueue.main, execute: workItem1)
        let workItem5 = DispatchWorkItem(qos: .unspecified, flags: .inheritQoS) {
            print("workItem 5,  thread: \(Thread.current)")
            sleep(1)
        workItem2.perform()
        workItem3.perform()
        workItem4.perform()
        workItem5.perform()

执行结果:

workItem 2,  thread: <NSThread: 0x600003f30bc0>{number = 1, name = main}
workItem 4,  thread: <NSThread: 0x600003f30bc0>{number = 1, name = main}
workItem 2 finished,  thread: <NSThread: 0x600003f75b40>{number = 3, name = (null)}
workItem 5,  thread: <NSThread: 0x600003f30bc0>{number = 1, name = main}
workItem 1,  thread: <NSThread: 0x600003f30bc0>{number = 1, name = main}

4. DispatchQueue

一个可以在应用程序的主线程或后台线程上连续或并发地管理任务的执行的对象。

open class DispatchQueue : DispatchObject {

DispatchQueue是一个FIFO队列,可以将任务以闭包或者DispatchWorkItem对象的形式提交给它。DispatchQueue会以串行或并行方式执行任务。提交给DispatchQueue的任务在系统管理的线程池中执行。

我们可以同步或异步地调度任务。当使用同步的方式调度一个任务时,后续的代码将停止执行,直到调度的任务执行结束。当使用异步的方式调度任务时,代码将继续执行。

切记在主队列上同步执行任务会导致死锁。

4.1 DispatchQueue种类

DispatchQueue有三种形式,具体看下表:

类型创建方式执行方式描述
主队列(main queue)DispatchQueue.main串行(Serial)与主线程有关,加入到主队列的任务项都会在主线程中执行。
全局队列(global queue)DispatchQueue.global()并行(Concurrent)在后台线程中执行任务,用于处理并发任务,具备开启多个线程的能力。
自定义队列(custom queue)init(label: String, qos: DispatchQoS, attributes: DispatchQueue.Attributes, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency, target: DispatchQueue?)串行/并行在后台线程中执行任务,默认为串行队列,即attributes不设值,当attributes为 .concurrent时,即为并行队列。串行队列只具备开启一个线程的能力,并行队列具备开启多个线程的能力。

4.2 创建DispatchQueue

三种创建DispatchQueue方式如下:

// 与当前进程的主线程相关联的调度队列, 即主队列。
class var main: DispatchQueue { get }
// 返回一个系统全局队列
class func global(qos: DispatchQoS.QoSClass = .default) -> DispatchQueue
* 创建一个派发队列。
* label:队列的唯一标识。
* qos  :决定了系统安排任务执行的优先级。
* attributes : 执行队列的执行方式,串行还是并行。
* autoreleaseFrequency : 指示队列自动释放对象的频率的常量。
* target : 计划执行blocks的目标队列。如果希望系统提供适合当前对象的队列,请指定DISPATCH_TARGET_QUEUE_DEFAULT。
convenience init(label: String, qos: DispatchQoS = .unspecified, attributes: DispatchQueue.Attributes = [], autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = .inherit, target: DispatchQueue? = nil)

示例如下:

func dispatchQueueDemo() {
		// 获取主队列
        let mainQueue = DispatchQueue.main
        // 获取全局队列
        let globalQueue = DispatchQueue.global()
        // 自定义一个并行队列
        let customConcurrentQueue = DispatchQueue(label: "com.queue.concurrent", qos: .unspecified, attributes: [.concurrent], autoreleaseFrequency: .inherit, target: nil)
        // 自定义一个串行队列,attributes不赋值即是串行。
        let serialConcurrentQueue = DispatchQueue(label: "com.queue.serial", qos: .unspecified, attributes: [], autoreleaseFrequency: .inherit, target: nil)
        print("mainQueue is \(mainQueue)")
        print("globalQueue is \(globalQueue)")
        print("customConcurrentQueue is \(customConcurrentQueue)")
        print("serialConcurrentQueue is \(serialConcurrentQueue)")

输出结果:

mainQueue is <OS_dispatch_queue_main: com.apple.main-thread>
globalQueue is <OS_dispatch_queue_global: com.apple.root.default-qos>
customConcurrentQueue is <OS_dispatch_queue_concurrent: com.queue.concurrent>
serialConcurrentQueue is <OS_dispatch_queue_serial: com.queue.serial>

4.3 DispatchQueue异步执行(串行/并行)

异步执行任务主要有以下这些方法:

// 立即执行派发的任务,并立即return。
func async(execute: DispatchWorkItem)
// 在指定的时间执行派发的任务,并立即return。
func asyncAfter(deadline: DispatchTime, execute: DispatchWorkItem)
// 在指定的时间执行派发的任务的block,并立即return。
func asyncAfter(deadline: DispatchTime, qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], execute work: @escaping @convention(block) () -> Void)
// 在指定的时间后执行派发的任务,并立即return。
func asyncAfter(wallDeadline: DispatchWallTime, execute: DispatchWorkItem)
// 在指定的时间后执行派发的任务的block,并立即return。
func asyncAfter(wallDeadline: DispatchWallTime, qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], execute work: @escaping @convention(block) () -> Void)

异步执行则强调调用的方法是异步方法,而并行或串行则强调队列的调度方式。
4.1节可知,队列如果细分则分为:
主队列
全局队列
自定义的串行队列
自定义的并行队列

下面看一下测试代码,分别用四种队列连续调用三次异步方法,并在闭包里面打印执行时间和线程。

func asyncDemo() {
        // 主队列调用三次异步函数
        DispatchQueue.main.async {
            print("Main queue async block 1, \tdate : \(Date()), current thread : \(Thread.current)")
            sleep(1)
        DispatchQueue.main.async {
            print("Main queue async block 2, \tdate : \(Date()), current thread : \(Thread.current)")
            sleep(1)
        DispatchQueue.main.async {
            print("Main queue async block 3, \tdate : \(Date()), current thread : \(Thread.current)")
            sleep(1)
        // 全局队列调用三次异步函数
        DispatchQueue.global().async {
            print("Global queue async block 1, \tdate : \(Date()), current thread : \(Thread.current)")
            sleep(1)
        DispatchQueue.global().async {
            print("Global queue async block 2, \tdate : \(Date()), current thread : \(Thread.current)")
            sleep(1)
        DispatchQueue.global().async {
            print("Global queue async block 3, \tdate : \(Date()), current thread : \(Thread.current)")
            sleep(1)
        // 自定义串行队列调用三次异步函数
        let serialQueue = DispatchQueue(label: "com.queue.serial", qos: .unspecified, attributes: [], autoreleaseFrequency: .inherit, target: nil)
        serialQueue.async {
            print("serialQueue async block 1, \tdate : \(Date()), current thread : \(Thread.current)")
            sleep(1)
        serialQueue.async {
            print("serialQueue async block 2, \tdate : \(Date()), current thread : \(Thread.current)")
            sleep(1)
        serialQueue.async {
            print("serialQueue async block 3, \tdate : \(Date()), current thread : \(Thread.current)")
            sleep(1)
        // 自定义并行队列调用三次异步函数
        let concurrentQueue = DispatchQueue(label: "com.queue.concurrent", qos: .unspecified, attributes: [.concurrent], autoreleaseFrequency: .inherit, target: nil)
        concurrentQueue.async {
            print("concurrentQueue async block 1, \tdate : \(Date()), current thread : \(Thread.current)")
            sleep(1)
        concurrentQueue.async {
            print("concurrentQueue async block 2, \tdate : \(Date()), current thread : \(Thread.current)")
            sleep(1)
        concurrentQueue.async {
            print("concurrentQueue async block 3, \tdate : \(Date()), current thread : \(Thread.current)")
            sleep(1)

输出结果如下:

Global queue async block 3, 	date : 2020-11-19 05:11:10 +0000, current thread : <NSThread: 0x600002cff940>{number = 4, name = (null)}
Global queue async block 1, 	date : 2020-11-19 05:11:10 +0000, current thread : <NSThread: 0x600002c83380>{number = 7, name = (null)}
Global queue async block 2, 	date : 2020-11-19 05:11:10 +0000, current thread : <NSThread: 0x600002ce9000>{number = 6, name = (null)}
concurrentQueue async block 3, 	date : 2020-11-19 05:11:10 +0000, current thread : <NSThread: 0x600002c893c0>{number = 3, name = (null)}
concurrentQueue async block 2, 	date : 2020-11-19 05:11:10 +0000, current thread : <NSThread: 0x600002c8b8c0>{number = 9, name = (null)}
concurrentQueue async block 1, 	date : 2020-11-19 05:11:10 +0000, current thread : <NSThread: 0x600002cf29c0>{number = 8, name = (null)}
serialQueue async block 1, 		date : 2020-11-19 05:11:10 +0000, current thread : <NSThread: 0x600002c88ac0>{number = 5, name = (null)}
Main queue async block 1, 		date : 2020-11-19 05:11:10 +0000, current thread : <NSThread: 0x600002cc8900>{number = 1, name = main}
serialQueue async block 2, 		date : 2020-11-19 05:11:11 +0000, current thread : <NSThread: 0x600002c88ac0>{number = 5, name = (null)}
Main queue async block 2, 		date : 2020-11-19 05:11:11 +0000, current thread : <NSThread: 0x600002cc8900>{number = 1, name = main}
serialQueue async block 3, 		date : 2020-11-19 05:11:12 +0000, current thread : <NSThread: 0x600002c88ac0>{number = 5, name = (null)}
Main queue async block 3, 		date : 2020-11-19 05:11:12 +0000, current thread : <NSThread: 0x600002cc8900>{number = 1, name = main}

结果总结:

  1. 全局队列和自定义并行队列里面的任务是并发执行的,且为每个任务开启了一个线程去执行任务。
  2. 自定义串行队列里面的任务是依次执行的(符合FIFO原则),且该队列开启了一个线程去执行里面所有的任务。
  3. 主队列里面的任务是依次执行的(符合FIFO原则),且主队列里面的任务都在主线程中执行。

4.4 DispatchQueue同步执行(串行/并行)

同步执行任务主要有以下这些方法:

// 提交一个workItem到当前队列,直到workItem的block结束后,再返回,继续执行后续代码。
func sync(execute workItem: DispatchWorkItem)
// 提交一个block到当前队列,直到block结束后,再返回,继续执行后续代码。
func sync(execute: () -> Void)
// 提交一个block到当前队列,block结束后,返回block的执行结果,然后继续执行后续代码。
func sync<T>(execute work: () throws -> T) rethrows -> T
// 提交一个block到当前队列,block结束后,返回block的执行结果,然后继续执行后续代码。
func sync<T>(flags: DispatchWorkItemFlags, execute work: () throws -> T) rethrows -> T

下面看一下测试代码,分别用全局队列 自定义的串行队列 自定义的并行队列调动同步方法,并在闭包里面打印执行时间和线程。

func syncDemo() {
        // 全局队列调用三次同步函数
        DispatchQueue.global().sync {
            print("Global queue sync block 1, \tdate : \(Date()), current thread : \(Thread.current)")
            sleep(1)
        DispatchQueue.global().sync {
            print("Global queue sync block 2, \tdate : \(Date()), current thread : \(Thread.current)")
            sleep(1)
        DispatchQueue.global().sync {
            print("Global queue sync block 3, \tdate : \(Date()), current thread : \(Thread.current)")
            sleep(1)
        // 自定义串行队列调用三次同步函数
        let serialQueue = DispatchQueue(label: "com.queue.serial", qos: .unspecified, attributes: [], autoreleaseFrequency: .inherit, target: nil)
        serialQueue.sync {
            print("serialQueue sync block 1, \tdate : \(Date()), current thread : \(Thread.current)")
            sleep(1)
        serialQueue.sync {
            print("serialQueue sync block 2, \tdate : \(Date()), current thread : \(Thread.current)")
            sleep(1)
        serialQueue.sync {
            print("serialQueue sync block 3, \tdate : \(Date()), current thread : \(Thread.current)")
            sleep(1)
        // 自定义并行队列调用三次同步函数
        let concurrentQueue = DispatchQueue(label: "com.queue.concurrent", qos: .unspecified, attributes: [.concurrent], autoreleaseFrequency: .inherit, target: nil)
        concurrentQueue.sync {
            print("concurrentQueue sync block 1, \tdate : \(Date()), current thread : \(Thread.current)")
            sleep(1)
        concurrentQueue.sync {
            print("concurrentQueue sync block 2, \tdate : \(Date()), current thread : \(Thread.current)")
            sleep(1)
        concurrentQueue.sync {
            print("concurrentQueue sync block 3, \tdate : \(Date()), current thread : \(Thread.current)")
            sleep(1)

输出结果如下:

Global queue sync block 1, 		date : 2020-11-19 05:31:13 +0000, current thread : <NSThread: 0x60000194ca00>{number = 1, name = main}
Global queue sync block 2, 		date : 2020-11-19 05:31:14 +0000, current thread : <NSThread: 0x60000194ca00>{number = 1, name = main}
Global queue sync block 3, 		date : 2020-11-19 05:31:15 +0000, current thread : <NSThread: 0x60000194ca00>{number = 1, name = main}
serialQueue sync block 1, 		date : 2020-11-19 05:31:16 +0000, current thread : <NSThread: 0x60000194ca00>{number = 1, name = main}
serialQueue sync block 2, 		date : 2020-11-19 05:31:17 +0000, current thread : <NSThread: 0x60000194ca00>{number = 1, name = main}
serialQueue sync block 3, 		date : 2020-11-19 05:31:18 +0000, current thread : <NSThread: 0x60000194ca00>{number = 1, name = main}
concurrentQueue sync block 1, 	date : 2020-11-19 05:31:19 +0000, current thread : <NSThread: 0x60000194ca00>{number = 1, name = main}
concurrentQueue sync block 2, 	date : 2020-11-19 05:31:20 +0000, current thread : <NSThread: 0x60000194ca00>{number = 1, name = main}
concurrentQueue sync block 3, 	date : 2020-11-19 05:31:21 +0000, current thread : <NSThread: 0x60000194ca00>{number = 1, name = main}

结果总结:

  1. 三种队列执行其任务的时候,没有开启新线程,而是直接在主线程执行任务。
  2. 同步函数执行的任务不分队列,按照调用顺序,依次执行。
  3. 主队列不能调用同步函数执行任务,否则死锁(示例代码中未写,详见4.5 死锁分析)。

4.5 死锁

4.5.1 主队列死锁

主队列调用同步函数会死锁。

func mainQueueLockDemo() {
        print("*** Start ***")
        DispatchQueue.main.sync {
            print("*** main sync function")
        print("*** End ***")

上面这个方法中,运行结果只会打印*** Start ***,然后程序就挂了。

原因分析:
凡是添加到主队列的任务,都会在主线程中执行。在将同步的sync block添加到主队列的时候,主队列里面有一个任务mainQueueLockDemo()正在执行,sync block是同步的,所以需要等mainQueueLockDemo()方法执行完才能开始执行,而sync block不执行的话,mainQueueLockDemo()方法是执行不完的,所以就存在相互等待对方执行完再执行的情况,即死锁。

看图示意:
在这里插入图片描述
下面将mainQueueLockDemo()方法改一下,将主队列改成全局队列,即可避免死锁。

func mainQueueLockDemo() {
        print("*** Start ***")
        DispatchQueue.global().sync {
            print("*** global sync function")
        print("*** End ***")

输出结果:

*** Start ***
*** global sync function
*** End ***

死锁原理了解后,这个就很好理解了,mainQueueLockDemo()是在主队列执行的,而sync block是在全局队列执行的,两个不同的队列,将sync block添加到全局队列后,在sync block任务前没有任务执行,所以sync block任务立即执行,此时,主队列的mainQueueLockDemo()任务在等待状态,当全局队列中的sync block任务结束后,主队列的mainQueueLockDemo()任务继续执行,直到结束。

看图示意:
在这里插入图片描述

4.5.2 其他队列死锁

主队列调用同步函数会死锁,那么其他队列就不会死锁了吗?如果使用不当,同样是会死锁的,下面具体来看一下。

1. 自定义串行队列

func serialQueueLockDemo() {
        let serialQueue = DispatchQueue(label: "serial")
        //死锁,原因:此嵌套会导致加入串行队列的两个同步任务相互等待,类似主队列死锁。
        serialQueue.sync {
            print("sync execution,  thread: \(Thread.current)")
            serialQueue.sync {
                print("sync execution,  thread: \(Thread.current)")
        //死锁,原因:此嵌套会导致加入串行队列的两个同步任务相互等待,与外层是否是异步无关。
        serialQueue.async {
            print("async execution,  thread: \(Thread.current)")
            serialQueue.sync {
                print("sync execution,  thread: \(Thread.current)")
        //不会死锁
        serialQueue.sync {
            print("sync execution,  thread: \(Thread.current)")
            serialQueue.async {
                print("async execution,  thread: \(Thread.current)")
        //不会死锁
        serialQueue.async {
            print("async execution,  thread: \(Thread.current)")
            serialQueue.async {
                print("async execution,  thread: \(Thread.current)")
  • 自定义串行队列嵌套同步任务,死锁。
  • 自定义串行队列嵌套异步任务,不会死锁。

2. 并行队列(全局队列、自定义并行队列)

// demo中指列举了全局队列的例子,自定义并行队列同全局队列。
func globalQueueLockDemo() {
        //不会死锁
        DispatchQueue.global().async {
            print("async execution  thread: \(Thread.current)")
            DispatchQueue.global().sync {
                print("sync execution,  thread: \(Thread.current)")
        //不会死锁
        DispatchQueue.global().sync {
            print("sync execution,  thread: \(Thread.current)")
            DispatchQueue.global().sync {
                print("sync execution,  thread: \(Thread.current)")
        //不会死锁
        DispatchQueue.global().sync {
            print("sync execution,  thread: \(Thread.current)")
            DispatchQueue.global().async {
                print("async execution,  thread: \(Thread.current)")
        //不会死锁
        DispatchQueue.global().async {
            print("async execution,  thread: \(Thread.current)")
            DispatchQueue.global().async {
                print("async execution,  thread: \(Thread.current)")

总结:
并行队列中不管是同步任务还是异步任务再嵌套同步任务还是异步任务都不会死锁。

5. DispatchGroup

DispatchGroup允许将同一队列或者不同队列的异步任务添加到组中,并且执行,当组内的所有任务都执行结束后,则执行组的回调函数通知程序以上异步任务都已经结束。

常用的方法如下:

public func notify(qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], queue: DispatchQueue, execute work: @escaping @convention(block) () -> Void)
public func notify(queue: DispatchQueue, work: DispatchWorkItem)
public func wait()
public func wait(timeout: DispatchTime) -> DispatchTimeoutResult
public func wait(wallTimeout timeout: DispatchWallTime) -> DispatchTimeoutResult
public func enter()
public func leave()

notify():组内所有任务执行完毕后,发送通知,可在当前队列监听,也可在主队列监听。
wait():阻塞当前线程,直到group所有任务执行完毕。
enter():显示的表示任务闭包加入了组中。
leave():显示的表示任务闭包执行结束,从组中移除。

5.1 nofity()函数使用

下面创建一个group和一个自定义的并行队列,在并行队列中添加四个异步任务,然后group监听执行结束通知。
注:

  1. 不建议用全局的并行队列,保不齐哪里还用了。
  2. 在队列中添加异步任务,同步任务会依次在主线程中执行,这样就没有并发。
func groupDemo() {
        let group = DispatchGroup()
        let customQueue = DispatchQueue(label: "com.queue.concurrent", qos: .unspecified, attributes: [.concurrent], autoreleaseFrequency: .inherit, target: nil)
        customQueue.async(group: group, qos: .unspecified, flags: .inheritQoS) {
            print("async execution 1,  thread: \(Thread.current)")
            sleep(1)
        customQueue.async(group: group, qos: .unspecified, flags: .inheritQoS) {
            print("async execution 2,  thread: \(Thread.current)")
            sleep(1)
        customQueue.async(group: group, qos: .unspecified, flags: .inheritQoS) {
            print("async execution 3,  thread: \(Thread.current)")
            sleep(1)
        customQueue.async(group: group, qos: .unspecified, flags: .inheritQoS) {
            print("async execution 4,  thread: \(Thread.current)")
            sleep(1)
        group.notify(qos: .unspecified, flags: .inheritQoS, queue: customQueue) {
            print("All tasks finished,  thread: \(Thread.current)")

输出结果如下:

async execution 1,  thread: <NSThread: 0x6000021c3bc0>{number = 7, name = (null)}
async execution 3,  thread: <NSThread: 0x6000021c1640>{number = 5, name = (null)}
async execution 4,  thread: <NSThread: 0x6000021c2540>{number = 4, name = (null)}
async execution 2,  thread: <NSThread: 0x6000021f1340>{number = 6, name = (null)}
All tasks finished,  thread: <NSThread: 0x6000021c3bc0>{number = 7, name = (null)}

如果改成在主队列监听通知,代码如下:

group.notify(qos: .unspecified, flags: .inheritQoS, queue: DispatchQueue.main) {
    print("All tasks finished,  thread: \(Thread.current)")

输出结果如下:

async execution 2,  thread: <NSThread: 0x600003e4dac0>{number = 7, name = (null)}
async execution 4,  thread: <NSThread: 0x600003e08600>{number = 5, name = (null)}
async execution 1,  thread: <NSThread: 0x600003e4e180>{number = 6, name = (null)}
async execution 3,  thread: <NSThread: 0x600003e0c080>{number = 4, name = (null)}
All tasks finished,  thread: <NSThread: 0x600003e40b40>{number = 1, name = main}

如果在主队列监听,那么监听函数的闭包即在主线程执行。

5.1 wait()函数使用

func groupDemo() {
        let group = DispatchGroup()
        let customQueue = DispatchQueue(label: "com.queue.concurrent", qos: .unspecified, attributes: [.concurrent], autoreleaseFrequency: .inherit, target: nil)
        customQueue.async(group: group, qos: .unspecified, flags: .inheritQoS) {
            print("async execution 1,  thread: \(Thread.current)")
            sleep(1)
        customQueue.async(group: group, qos: .unspecified, flags: .inheritQoS) {
            print("async execution 2,  thread: \(Thread.current)")
            sleep(1)
        customQueue.async(group: group, qos: .unspecified, flags: .inheritQoS) {
            print("async execution 3,  thread: \(Thread.current)")
            sleep(1)
        customQueue.async(group: group, qos: .unspecified, flags: .inheritQoS) {
            print("async execution 4,  thread: \(Thread.current)")
            sleep(1)
//        group.wait()
        print("All tasks finished,  thread: \(Thread.current)")

上面demo中,先把group.wait()注释掉,输出结果中,“All tasks finished”这句话提前调用了,如下:

async execution 1,  thread: <NSThread: 0x600000a6a780>{number = 8, name = (null)}
All tasks finished,  thread: <NSThread: 0x600000a78900>{number = 1, name = main}
async execution 3,  thread: <NSThread: 0x600000a3e0c0>{number = 7, name = (null)}
async execution 2,  thread: <NSThread: 0x600000a34f40>{number = 5, name = (null)}
async execution 4,  thread: <NSThread: 0x600000a3c300>{number = 4, name = (null)}

如果将group.wait()注释取消,wait()会阻塞当前线程,直到group中的任务都执行结束了,看下面结果:

async execution 3,  thread: <NSThread: 0x600002582180>{number = 6, name = (null)}
async execution 1,  thread: <NSThread: 0x60000258eb80>{number = 5, name = (null)}
async execution 2,  thread: <NSThread: 0x6000025def00>{number = 4, name = (null)}
async execution 4,  thread: <NSThread: 0x6000025c4e00>{number = 7, name = (null)}
All tasks finished,  thread: <NSThread: 0x6000025cc040>{number = 1, name = main}

5.3 enter()和leave()函数使用

func groupDemo() {
        let group = DispatchGroup()
        let customQueue = DispatchQueue(label: "com.queue.concurrent", qos: .unspecified, attributes: [.concurrent], autoreleaseFrequency: .inherit, target: nil)
        group.enter()
        customQueue.async {
            print("async execution 1,  thread: \(Thread.current)")
            sleep(1)
            group.leave()
        group.enter()
        customQueue.async {
            print("async execution 2,  thread: \(Thread.current)")
            sleep(1)
            group.leave()
        group.enter()
        customQueue.async {
            print("async execution 3,  thread: \(Thread.current)")
            sleep(1)
            group.leave()
        group.enter()
        customQueue.async {
            print("async execution 4,  thread: \(Thread.current)")
            sleep(1)
            group.leave()
        let workItem = DispatchWorkItem {
            print("All tasks finished,  thread: \(Thread.current)")
        group.notify(queue: DispatchQueue.main, work: workItem)

6. DispatchQoS

DispatchQoS调度优先级:应用在任务上的服务质量或执行优先级。可以用来修饰DispatchWorkItem、DispatchQueue。
DispatchQoS类型如下表:

类型描述
userInteractive与用户交互相关的任务,比如动画,事件处理,UI处理等。
userInitiated用户主动发起的任务,要比较重视。
default默认任务,正常处理即可。
utility用户没有主动关注的任务。
background不太重要的维护、清理等任务,有空能处理完就行
unspecified没有指定优先级,看情况处理了。

上述列表中,优先等级由上至下逐渐降低。

7. DispatchWorkItemFlags

工作项的一组行为,例如它的服务质量类,以及是否创建一个障碍或产生一个新的分离线程。
其可选值有:

// 设置工作项的属性采用当前执行上下文的QoS。
static let assignCurrentContext: DispatchWorkItemFlags
// 使工作项在提交到并行队列时充当屏障。
static let barrier: DispatchWorkItemFlags
// 将工作项的QoS与当前执行上下文分离。
static let detached: DispatchWorkItemFlags
// 强制使用当前上下文的QoS,而不是队列的QoS。
static let enforceQoS: DispatchWorkItemFlags
// 选择与当前执行上下文关联的QoS,如果添加到队列中,则采用队列的QoS。
static let inheritQoS: DispatchWorkItemFlags
// 在不指定QoS的情况下执行工作项,由调用线程或队列来指定。
static let noQoS: DispatchWorkItemFlags

上面众多项中,最常用的也就是barrier,下面看一下它的用法。
如果一个工作项的qos属性设置为barrier,那么在这个工作项之后的任务会在这个工作项之前的任务执行结束后再执行。
说起来比较拗口,下面看代码:

func barrierDemo() {
        let workItem1 = DispatchWorkItem(qos: .unspecified, flags: .inheritQoS) {
            print("workItem 1,  thread: \(Thread.current)")
            sleep(1)
        let workItem2 = DispatchWorkItem(qos: .unspecified, flags: .inheritQoS) {
            print("workItem 2,  thread: \(Thread.current)")
            sleep(1)
        let workItem3 = DispatchWorkItem(qos: .unspecified, flags: .inheritQoS) {
            print("workItem 3,  thread: \(Thread.current)")
            sleep(1)
        let workItem4 = DispatchWorkItem(qos: .unspecified, flags: .inheritQoS) {
            print("workItem 4,  thread: \(Thread.current)")
            sleep(1)
        let workItem5 = DispatchWorkItem(qos: .unspecified, flags: .inheritQoS) {
            print("workItem 5,  thread: \(Thread.current)")
            sleep(1)
        let customQueue = DispatchQueue(label: "com.queue.concurrent", qos: .unspecified, attributes: [.concurrent], autoreleaseFrequency: .inherit, target: nil)
        customQueue.async(execute: workItem1)
        customQueue.async(execute: workItem2)
        customQueue.async(execute: workItem3)
        customQueue.async(execute: workItem4)
        customQueue.async(execute: workItem5)

上面创建了一并行队列,并异步执行了五个任务,输出结果如下:

workItem 1,  thread: <NSThread: 0x60000051c140>{number = 5, name = (null)}
workItem 5,  thread: <NSThread: 0x60000051c8c0>{number = 3, name = (null)}
workItem 2,  thread: <NSThread: 0x6000005162c0>{number = 4, name = (null)}
workItem 4,  thread: <NSThread: 0x600000519cc0>{number = 8, name = (null)}
workItem 3,  thread: <NSThread: 0x600000566e80>{number = 7, name = (null)}

如果将workItem3的初始化修改一下,如下:

let workItem3 = DispatchWorkItem(qos: .unspecified, flags: .barrier) {
     print("workItem 3,  thread: \(Thread.current)")
     sleep(1)

那么workItem3将起到屏障的作用,workItem1workItem2workItem3之前执行,workItem4和workItem5workItem3之后执行。看一下输出结果:

workItem 2,  thread: <NSThread: 0x600001dccb00>{number = 2, name = (null)}
workItem 1,  thread: <NSThread: 0x600001dcdcc0>{number = 4, name = (null)}
workItem 3,  thread: <NSThread: 0x600001dcdcc0>{number = 4, name = (null)}
workItem 4,  thread: <NSThread: 0x600001dcdcc0>{number = 4, name = (null)}
workItem 5,  thread: <NSThread: 0x600001dccb00>{number = 2, name = (null)}

8. 结束语

本文主要讲解了GCD的概念,DispatchQueue、DispatchGroup以及死锁等,希望不仅帮助自己,也能帮助他人更好的使用Swift GCD,如果想精通,还需在后续的开发中不断地摸索,同时也欢迎各位朋友就本文章做出指正。

如果觉得文章对你有用,不妨给个赞,关注一下,更多好用文章还在继续更新中。

本篇文章出自https://blog.csdn.net/guoyongming925的博客,如需转载,请标明出处。

这本书教你如何为你的iOS应用程序编写高性能和并发代码。 了解什么是并发,为什么你甚至想在你的应用程序中使用它? 了解Grand Central Dispatch,Apple对C的libdispatch的实现,也称为GCD,因为它是排队任务并行运行的最简单方法之一。 然后,当GCD没有完全削减它时,采取操作和操作队列; 您将学习如何进一步定制和重用您的并发工作。 然后,您将学习在开发并发应用程序时可能遇到的常见并发问题,例如Race Conditions,Deadlocks等。 最后,了解线程和线程清理程序以及各种与线程相关的概念以及这些概念如何与您在本书中积累的知识相关联。 您还将学习如何在出现问题时使用Thread Sanitizer来简化调试。
国庆长假,不想一行白鹭上青天,回家挤在在最中间。就把征战已久的古董pro挖出,硬生生升到了macOS Sierrra,跑起了 Xcode 8 ,写起了swift。 先来一波实用干货。 swift 闭包项目的实际应用,和objective-c 的 Block 类似。HttpTool 工具类import UIKitclass HttpTool: NSObject { // 闭包的类型:
Swift3开始GCD的API就发生了很大的变化,更加简洁,使用起来更方便。像我们经常开启一个异步线程处理事情然后切回主线程刷新UI操作,这里就变的非常简单了。 DispatchQueue.global().async { // do async task DispatchQueue.main.async { // update UI
GCD(Grand Central Dispatch)是基于C语言开发的一套多线程开发机制,也是目前苹果官方推荐的多线程开发方法。相对于 NSThread 和 NSOperation,GCD抽象层次最高,使用起来也最简单,只是它基于C语言开发,并不像NSOperation是面向对象的开发,而是完全面向过程的。这种机制相比较于前面两种多线程开发方式最显著的优点就是它对于多核运算更加有效。 GCD......
之前遇到一个问题,一个请求需要在另一个请求获得的参数。这个时候最开始的办法是把第二个请求写在第一个请求的回调里,但是这样的话,两个请求就很紧密的耦合在一起了。这个时候可以使用信号量来使他们分离开来。 先看下相关的3个方法: dispatch_semaphore_t dispatch_semaphore_create(long value):方法接收一个long类型的参数, 返回一个disp...
iOS Swift中连接扫描枪的代码主要分为两个部分:Bluetooth连接和接收扫描数据。 首先,需要在应用中引入CoreBluetooth库。在ViewController中创建一个BLEManager类,并像下面这样定义其属性和方法: ```swift class BLEManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { var centralManager: CBCentralManager? var peripheral: CBPeripheral? var characteristic:CBCharacteristic? override init() { super.init() centralManager = CBCentralManager(delegate: self, queue: nil) func centralManagerDidUpdateState(_ central: CBCentralManager) { if central.state == .poweredOn { central.scanForPeripherals(withServices:nil, options: nil) print("Scanning started") } else { print("Bluetooth not available.") func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { if (peripheral.name == "Your peripheral name") { self.centralManager?.stopScan() self.peripheral = peripheral self.peripheral?.delegate = self self.centralManager?.connect(self.peripheral!, options: nil) func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { if peripheral == self.peripheral { peripheral.discoverServices(nil) print("Connected to your peripheral") func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { for service in peripheral.services! { peripheral.discoverCharacteristics(nil, for: service) func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { for characteristic in service.characteristics! { if characteristic.uuid == CBUUID(string: "Your characteristic UUID") { self.characteristic = characteristic peripheral.setNotifyValue(true, for: characteristic) print("Characteristic found") func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { if characteristic == self.characteristic { if let data = characteristic.value { let string = String(data: data, encoding: .utf8)! print("Scanned barcode is: \(string)") 接着,在ViewController中初始化BLEManager并调用centralManager的方法: ```swift var bleManager: BLEManager? override func viewDidLoad() { super.viewDidLoad() bleManager = BLEManager() 最后,需要将扫描枪的UUID和特征UUID替换为自己的。这样,应用就能连接扫描枪并接收扫描数据了。
升级Xcode12.3报错(Building for iOS Simulator, but the linked and embedded framework ‘***‘ was built ...) 16735 Xcode12 building for iOS Simulator, but linking in dylib built for iOS, file for architecture arm64 Xcode12 building for iOS Simulator, but linking in dylib built for iOS, file for architecture arm64 zzzzzz9527: xcode13还报这个错 Xcode12.5编译RN项目报错(Cannot initialize a parameter of type ‘NSArray<id<RCTBridgeModule>> *‘ with an...) Daniel_Coder: 私信你了,希望有所帮助。 Xcode12.5编译RN项目报错(Cannot initialize a parameter of type ‘NSArray<id<RCTBridgeModule>> *‘ with an...) 七月星辰: 博主求助这个Podfile文件在哪里?我的也是rn项目下对应的文件丢在xcode里然后编译就报这个错了 升级Xcode12.3报错(Building for iOS Simulator, but the linked and embedded framework ‘***‘ was built ...) Daniel_Coder: 可以参考一下这篇文章《Xcode12.5打包Framework报错处理》,或者直接打包成xcframework,建议用xcframework. 升级Xcode12.3报错(Building for iOS Simulator, but the linked and embedded framework ‘***‘ was built ...) 智域智联科技: 用Swift写的Framework,每次Xcode升级,都会报Swift版本不兼容的错误,目前的解决方式就是用最新的Xcode重新打包Framework,请问找到解决方案了吗大佬