GCD详解

基本概念

  • 并行: 多个任务同时执行
  • 串行:一个任务执行完成后,再执行下一个任务
  • 同步:在当前线程中执行任务,不会开启新线程
  • 异步:在新的线程中执行任务

在这里插入图片描述
在这里插入图片描述

GCD的Dispatch Queues

Grand Central Dispatch 的基本概念就是dispatch queue调度队列,可以是并发的,可以是串行的。调度队列有三种类型:

  • The main queue 串行队列,和应用程序的主线程功能相同。通过DispatchQueue.main来获得main队列
  • Global queues 全局队列是并行队列,全局队列有高、中、低、后台4个优先级,默认优先级是中。通过DispatchQueue.global来获得全局队列
  • 用户线程队列 通过DispatchQueue.init来获得用户线程队列,以串行/并行的队列。
    在这里插入图片描述

在这里插入图片描述

DispatchGroup

DispatchGroup对多个任务进行管理和调度。enter与leave相对应。
DispatchGroup

DispatchGroup-wait

当前线程等待,直至DispatchGroup任务调度结束,堵塞当前线程(主线程)。

    let workdingGroup = DispatchGroup()
    let workdingQueue = DispatchQueue(label: "queue")//默认为串行队列
    
    workdingGroup.enter()
    workdingQueue.async {
        Thread.sleep(forTimeInterval: 1)
        print("第一个异步操作")
        workdingGroup.leave()
    }
    
    workdingGroup.enter()
    workdingQueue.async {
        Thread.sleep(forTimeInterval: 1)
        print("第二个异步操作")
        workdingGroup.leave()
    }
    
    print("主线程操作")
    workdingGroup.wait()
    print("DispatchGroup调度堵塞结束")

//输出
主线程操作
第一个异步操作
第二个异步操作
DispatchGroup调度堵塞结束

DispatchGroup-notify

监听DispatchGroup任务完成状态,完成后触发闭包回调,不堵塞当前线程(主线程)。

	let workdingGroup = DispatchGroup()
    let workdingQueue = DispatchQueue(label: "queue")//默认为串行队列
    
    workdingGroup.enter()
    workdingQueue.async {
        Thread.sleep(forTimeInterval: 1)
        print("第一个异步操作")
        workdingGroup.leave()
    }
    
    workdingGroup.enter()
    workdingQueue.async {
        Thread.sleep(forTimeInterval: 1)
        print("第二个异步操作")
        workdingGroup.leave()
    }
    
    print("主线程操作")
    workdingGroup.notify(queue: workdingQueue) {
        print("两个异步任务完成")
    }
    print("DispatchGroup调度不阻塞主线程")

//输出
主线程操作
DispatchGroup调度不阻塞主线程
第一个异步操作
第二个异步操作
两个异步任务完成

实例:实现线程安全的Array读与写

		var array = Array(0...10000)
        
        let queue1 = DispatchQueue(label: "quque1", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
        
        let queue2 = DispatchQueue(label: "quque2", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
        
        func getLastItem() -> Int? {
            let count = array.count
            if count > 0 {
                return array[count - 1]
            }
            return nil
        }
        
        func removeLastItem() {
            array.removeLast()
        }
        
        
        queue1.async {
            for _ in 0...10000 {
                if let lastNum = getLastItem() {
                    print(lastNum)
                }
            }
        }
        
        queue2.async {
            for _ in 0...10000 {
                removeLastItem()
            }
        }

//输出
10000
9856
9850
9845
9841
9837
9834
9830
9830
9830
9830
9830
9830
9830
9830
9830
9817
9812
9809
Fatal error: Index out of range

由于读与写操作是异步执行的,会存在要访问的下标之前已经删除元素而出现下标越界的情况。所以需要对两个并行异步线程进行安全管理。

第一种方案 线程加锁

var array = Array(0...10000)
        
        let queue1 = DispatchQueue(label: "quque1", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
        
        let queue2 = DispatchQueue(label: "quque2", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
        
        let lock = NSLock()
        
        func getLastItem() -> Int? {
            lock.lock()
            let count = array.count
            if count > 0 {
                return array[count - 1]
            }
            lock.unlock()
            return nil
        }
        
        func removeLastItem() {
            lock.lock()
            array.removeLast()
            lock.unlock()
        }
        
        
        queue1.async {
            for _ in 0...10000 {
                if let lastNum = getLastItem() {
                    print(lastNum)
                }
            }
        }
        
        queue2.async {
            for _ in 0...10000 {
                removeLastItem()
            }
        }

虽然不会出现数组访问越界的情况,但是如果对读操作多次使用而每次使用都要进行上锁和解锁操作,无疑会对性能产生影响。

第二种方法:barrier async

在这里插入图片描述
等待barrier async 之前的函数完成,再完成barrier async ,再完成barrier async 之后的函数。

var array = Array(0...10000)
        
        let queue1 = DispatchQueue(label: "quque1", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
        
        let queue2 = DispatchQueue(label: "quque2", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
        
        let arrayQueue = DispatchQueue(label: "quque3", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
        
        func getLastItem() -> Int? {
            return arrayQueue.sync { () -> Int? in
                if array.count > 0 {
                    return array[array.count - 1]
                }
                return nil
            }
        }
        
        func removeLastItem() {
            let workItem = DispatchWorkItem(qos: DispatchQoS.default, flags: DispatchWorkItemFlags.barrier) {
                array.removeLast()
            }
            arrayQueue.async(execute: workItem)
        }
        
        
        queue1.async {
            for _ in 0...10000 {
                if let item = getLastItem() {
                    print(item)
                }
            }
        }
        
        queue2.async {
            for _ in 0...10000 {
                removeLastItem()
            }
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值