iOS GCD学习总结(一)

一. GCD 简介

充分利用多核来处理相关任务,它是一个在线程池模式的基础上执行的并发任务

二.GCD 任务和队列

任务:

就是在GCD里的block,执行任务的方式有两种,『同步』和『异步』。

同步执行(sync):
不具备开起线程的能力,同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行。

异步执行(async):
具备开起线程的能力,异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务

举个例子:需要打电话给小明和小白。
同步执行:只有一个手机(当前线程),必须等待和小明的同话结束才能打给小白
异步执行:有两个或者多个手机(多个线程),不必等待,用第二个手机打给小白

注意:异步执行(async)虽然具有开启新线程的能力,但是并不一定开启新线程

队列(Dispatch Queue):

用来存放任务的队列,队列都是先进先出的,新的任务被追加到队列的未尾,读取执行的任务都是从队列的开头取出任务
例:即将取出任务1执行【任务1,任务2,任务3】添加新任务4追加到队列末尾,【】代表队列

在 GCD 中有两种队列:『串行队列』 和 『并发队列』。两者的主要区别是:执行顺序不同,以及开启线程数不同。
『串行队列(Serial Dispatch Queue)』:一个任务执行完成才能执行下一个任务
『并发队列(Serial Dispatch Queue)』:多个任务可以同时执行(该能力只在并发队列异步执行下才有)

三.GCD 使用

1.创建队列(串行或并发队列)
2.将任务添加到队列中(同步或异步),然后系统会自动执行队列中的任务

3.1 队列创建和系统已有的队列获取
    //串行队列 
    let syncQueue:DispatchQueue = DispatchQueue.init(label: "SYNC_QUEUE")//默认串行队列
    //并发队列
    let asyncQqueue:DispatchQueue = DispatchQueue.init(label: "ASYNC_QUEUE", attributes: .concurrent)
    //主队列:实质上就是一个普通的串行队列,当前编写的代码都会放在主线程上执行
    let mainQueue:DispatchQueue = DispatchQueue.main
   //全局并发队列:实质上就是一个普通的并发队列
    let globalQueue:DispatchQueue = DispatchQueue.global()
3.2任务创建

queue取决于上面四种队列

        //同步执行任务的创建
        queue.sync {
            //任务的具体实现
        }
        //异步执行任务的创建
        queue.async {
            //任务的具体实现
        }

四种队列和两种任务执行方式的不同组合如下:

同步+串行队列
异步+串行队列
同步+并发队列
异步+并发队列
同步+主队列
异步+主队列
同步+全局队列
异步+全局队列

区别如下:
区别图.png

注意:不能在该串行队列中同步添加任务到该串行队列,否则会造成死锁,

    func deadLock(){
        /****** 在该串行队列中同步添加任务到该串行队列才会造成死锁  ******/
        DispatchQueue.main.sync {//死锁
            print("任务1")
        }
    }

    func deadLock(){
        /****** 在该串行队列中同步添加任务到该串行队列才会造成死锁  ******/
        queueSync.sync {
            print("任务1")
            self.queueSync.sync {//死锁
                print("任务2")
            }
            print("end")
        }
    }

死锁原因:串行队列中追加的同步任务1,当同步任务1正执行时,添加同步任务2,此时需要等待任务1执行完毕才能执行任务2,而任务1执行完毕需要等待任务2执行完毕,相互等待,最终死锁,主队列也是如此,死锁这种情况多见于同一个串行队列的嵌套使用。

以下是几种不同组合的例子:

同步异步+串行队列
    /**
    * 同步执行 + 串行队列
    * 特点:在当前线程中执行任务,不会开启新线程,执行完一个任务,再执行下一个任务。
    */
    func syncSerial(){
        print("currentThread---\(Thread.current)")
        print("begin")
        let queue:DispatchQueue = DispatchQueue.init(label: "syncSerial")
        queue.sync {
            sleep(4)
            print("1---\(Thread.current)")
        }
        
        queue.sync {
            sleep(2)
            print("2---\(Thread.current)")
        }
        
        queue.sync {
            print("3---\(Thread.current)")
        }
        
        print("end")
    }
输出:currentThread---<NSThread: 0x170071c80>{number = 1, name = main}
begin
1---<NSThread: 0x170071c80>{number = 1, name = main}
2---<NSThread: 0x170071c80>{number = 1, name = main}
3---<NSThread: 0x170071c80>{number = 1, name = main}
end

同步(sync)执行任务不具有开启线程的能力,所以共用一个主线程,一个任务执行完毕才能执行下一个任务

    /**
    * 异步执行 + 串行队列
    * 特点:开启一条新线程,执行完一个任务,再执行下一个任务,主线程不再等待
    */
    func asyncSerial(){
        print("currentThread---\(Thread.current)")
        print("begin")
        let queue:DispatchQueue = DispatchQueue.init(label: "asyncSerial")
        queue.async {
            sleep(4)
            print("1---\(Thread.current)")
        }
        
        queue.async {
            sleep(2)
            print("2---\(Thread.current)")
        }
        
        queue.async {
            print("3---\(Thread.current)")
        }
        
        print("end")
    }
currentThread---<NSThread: 0x174074880>{number = 1, name = main}
begin
end
1---<NSThread: 0x1702684c0>{number = 3, name = (null)}
2---<NSThread: 0x1702684c0>{number = 3, name = (null)}
3---<NSThread: 0x1702684c0>{number = 3, name = (null)}

异步(async)执行任务具有开启新线程的能力,且主线程不等待输出end,但串行队列只开启一条线程,且任务一个执行完毕才能执行下一个

同步异步+并发队列
    /**
    * 同步执行 + 并发队列
    * 特点:在当前线程中执行任务,不会开启新线程,执行完一个任务,再执行下一个任务。
    */
    func syncConcurrent(){
        print("currentThread---\(Thread.current)")
        print("begin")
        let queue:DispatchQueue = DispatchQueue.init(label: "syncConcurrent", attributes: .concurrent)
        queue.sync {
            sleep(4)
            print("1---\(Thread.current)")
        }
        
        queue.sync {
            sleep(2)
            print("2---\(Thread.current)")
        }
        
        queue.sync {
            print("3---\(Thread.current)")
        }
        
        print("end")
    }
>输出:
>currentThread---<NSThread: 0x1700730c0>{number = 1, name = main}
>begin
>1---<NSThread: 0x1700730c0>{number = 1, name = main}
>2---<NSThread: 0x1700730c0>{number = 1, name = main}
>3---<NSThread: 0x1700730c0>{number = 1, name = main}
>end

同步(sync)执行任务不具有开启线程的能力,并发列队只在异步(async)执行任务的时候才会开启新的线程且并发执行任务,所以当前共用一个线程(主线程),同步(sync)执行任务需要一个执行完毕才能执行下一个

    /**
    * 异步执行 + 并发队列
    * 特点:队列开启新线程执行任务,主线程不再等待
    */
    func asyncConcurrent(){
        print("asyncConcurrent---\(Thread.current)")
        print("begin")
        let aqueue:DispatchQueue = DispatchQueue.init(label: "asyncConcurrent", attributes: .concurrent)
        aqueue.async {
            sleep(3)
            print("1---\(Thread.current)")
        }
        
        aqueue.async {
            sleep(2)
            print("2---\(Thread.current)")
        }
        
        aqueue.async {
            sleep(1)
            print("3---\(Thread.current)")
        }
        
        print("end")
    }
>输出:asyncConcurrent---<NSThread: 0x17006f740>{number = 1, name = main}
>begin
>end
>3---<NSThread: 0x170263840>{number = 3, name = (null)}
>2---<NSThread: 0x170263ac0>{number = 4, name = (null)}
>1---<NSThread: 0x170263b80>{number = 5, name = (null)}

异步(async)执行任务具有开启线程能力,所以当前主线程不等待继续执行下面代码输出end,并发队列开启新的线程并在该线程中执行任务

同步异步+主队列
    /**
    * 同步执行 + 主队列
    * 特点:死锁
    */
    func syncMain(){
        print("syncMain---\(Thread.current)")
        print("begin")
        DispatchQueue.main.sync {
            //任务1
            print("1---\(Thread.current)")
        }
        print(" end")
    }
syncMain---<NSThread: 0x17006dec0>{number = 1, name = main}
begin
(lldb) 

主队列是一个串行队列。这里是因为当系统执行syncMain方法,相当于把syncMain任务放到主队列里,而执行syncMain的过程中添加了任务1到主队列中,syncMain执行完毕需要待任务1执行完,任务1执行完需要等待syncMain执行完,互相等待,所以死锁

    /**
    * 异步执行 + 主队列
    * 特点:在当前线程中执行任务,不会开启新线程,执行完一个任务,再执行下一个任务。
    */
    func asyncMain(){
        print("asyncMain---\(Thread.current)")
        print("begin")
        let aqueue:DispatchQueue = DispatchQueue.main
        aqueue.async {
            sleep(3)
            print("1---\(Thread.current)")
        }
        
        aqueue.async {
            sleep(2)
            print("2---\(Thread.current)")
        }
        
        aqueue.async {
            sleep(1)
            print("3---\(Thread.current)")
        }
        
        print("end")
    }
asyncMain---<NSThread: 0x174067300>{number = 1, name = main}
begin
end
1---<NSThread: 0x174067300>{number = 1, name = main}
2---<NSThread: 0x174067300>{number = 1, name = main}
3---<NSThread: 0x174067300>{number = 1, name = main}

异步执行不会做任何等待,可以继续执行任务,虽然异步执行可以开启新的线程,但主队列是串行队列本身已存在主线程,所以此处不再开启新的线程。

全局并发队列跟普通并发队列并无区别

相关简书:
iOS GCD学习总结(二)
iOS 线程同步方案学习总结
信号量semaphore学习总结
iOS dispatch_barrier_sync实现多读单写
NSOperation和NSOperationQueue学习总结

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,701评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,649评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,037评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,994评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,018评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,796评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,481评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,370评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,868评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,014评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,153评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,832评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,494评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,039评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,437评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,131评论 2 356