iOS GCD知识

为什么要用GCD

更好的利用多核、更好的利用并发

什么时候用到GCD

解决耗时、阻塞主队列(主线程)、完成并发任务时用到GCD。

1、GCD简介

GCDGrand Central Dispatch简称,是一套低层API,提供了一种新的方法来进行并发程序编写。从基本功能上讲,GCD有点像NSOperationQueue,他们都允许程序将任务切分为多个单一任务然后提交至工作队列来并发地或者串行地执行。GCD比之 NSOpertionQueue更底层更高效,并且它不是Cocoa框架的一部分。

GCD是为多核的并行运算提出的解决方案, 除了代码的平行执行能力,GCD还提供高度集成的事件控制系统。可以设置句柄来响应文件描述符、mach ports(Mach port 用于 OS X上的进程间通讯)、进程、计时器、信号、用户生成事件。这些句柄通过GCD来并发执行。

GCD
的API很大程度上基于block,当然,GCD也可以脱离block来使用,比如使用传统c机制提供函数指针和上下文指针。实践证明,当配合block使用时,GCD非常简单易用且能发挥其最大能力。


2、GCD的工作原理

让程序平行排队的特定任务,根据可用的处理资源,安排他们在任何可用的处理器核心上执行任务。一个任务可以是一个函数(function)或者是一个block。GCD的底层依然是用线程实现,不过这样可以让程序员不用关注实现的细节。

核心概念

(1)任务:执行什么操作。在 GCD中就是一个 Block,所以添加任务十分方便。任务有两种执行方式: 同步执行和异步执行,同步(sync) 和 异步(async) 的主要区别在于会不会阻塞当前线程,直到 Block 中的任务执行完毕。

  • 同步执行:它会阻塞当前线程并等待 Block 中的任务执行完毕,然后当前线程才会继续运行。
  • 异步执行:当前线程会直接往下执行,它不会阻塞当前线程。

(2)队列:用来存放任务。系统提供了两个队列,一个是主队列MainDispatchQueue,一个全局队列GlobalDispatchQueue, 即串行队列 和 并行队列。

  • 串行队列:放到串行队列的任务,GCD 会 FIFO(先进先出) 地取出来一个,执行一个,然后取下一个,这样一个一个的执行。会将任务插入主线程的RunLoop当中去执行,我们可以使用它来更新UI。
  • 并行队列:放到并行队列的任务,GCD 也会 FIFO的取出来,但不同的是,它取出来一个就会放到别的线程,然后再取出来一个又放到另一个的线程。这样由于取的动作很快,忽略不计,看起来,所有的任务都是一起执行的。不过需要注意,GCD 会根据系统资源控制并行的数量,所以如果任务很多,它并不会让所有任务同时执行。有高、默认、低和后台4个优先级。

3、GCD的优势

  • 易用: GCD比之NSThread更简单易用。由于GCD基于work unit而非像thread那样基于运算,所以GCD可以控制诸如等待任务结束、监视文件描述符、周期执行代码以及工作挂起等任务。基于block的血统导致它能极为简单得在不同代码作用域之间传递上下文。
  • 效率: GCD被实现得如此轻量和优雅,使得它在很多地方比之专门创建消耗资源的线程更实用且快速。这关系到易用性:导致GCD易用的原因有一部分在于你可以不用担心太多的效率问题而仅仅使用它就行了。
  • 性能: GCD自动根据系统负载来增减线程数量,这就减少了上下文切换以及增加了计算效率。

串行、并行、同步、异步

串行:队列中的任务一个一个顺序执行。(一个任务执行->等待返回结果->下一个任务执行)
并行:队列中的任务一个一个顺序执行。(一个任务一个任务的顺序执行,不用等待返回结果)
同步:就是在发出一个功能调用时,在没有得到结果之前,该调用就不继续往下运行(调用)。也就是必须一件一件事做,等前一件做完了才能做下一件事。(一个线程,当前线程。)
异步:异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的任务在完成后,通过状态、通知和回调来通知调用者。(多个线程,开辟出来的新线程。)

串行队列同步:队列中的任务一个一个顺序执行。不会开辟新线程。(一个任务执行->等待返回结果->下一个任务执行)

//获取当前线程
NSLog(@"主线程 = %@",[NSThread currentThread]);

{
    //串行队列同步
    //dispatch_queue_create(const char *label, dispatch_queue_attr_t attr) 
    //label:queue的标记。dispatch_queue_attr_t:创建的队列是串行还是并行。DISPATCH_QUEUE_CONCURRENT 并行,DISPATCH_QUEUE_SERIAL / NULL 串行
    dispatch_queue_t queue = dispatch_queue_create("zhouhao", NULL);
    dispatch_sync(queue, ^{
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"当前线程 = %@ -- i = %ld",[NSThread currentThread],(long)i);
        }
    });
    
    dispatch_sync(queue, ^{
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"    当前线程 = %@ -- j = %ld",[NSThread currentThread],(long)i);
        }
    });
}

//Output
** Demo[5072:269390] ****主线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main}**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- i = 0**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- i = 1**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- i = 2**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- i = 3**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- i = 4**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- i = 5**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- i = 6**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- i = 7**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- i = 8**
** Demo[5072:269390] ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- i = 9**
** Demo[5072:269390]     ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- j = 0**
** Demo[5072:269390]     ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- j = 1**
** Demo[5072:269390]     ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- j = 2**
** Demo[5072:269390]     ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- j = 3**
** Demo[5072:269390]     ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- j = 4**
** Demo[5072:269390]     ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- j = 5**
** Demo[5072:269390]     ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- j = 6**
** Demo[5072:269390]     ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- j = 7**
** Demo[5072:269390]     ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- j = 8**
** Demo[5072:269390]     ****当前线程**** = <NSThread: 0x7fa1b0d08000>{number = 1, name = main} -- j = 9**

串行队列异步:任务一个一个的顺序执行。会开辟新线程。(一个任务执行->等待返回结果->下一个任务执行)

//获取当前线程
NSLog(@"主线程 = %@",[NSThread currentThread]);

{
    //串行队列异步
    //dispatch_queue_create(const char *label, dispatch_queue_attr_t attr) 
    //label:queue的标记。dispatch_queue_attr_t:创建的队列是串行还是并行。DISPATCH_QUEUE_CONCURRENT 并行,DISPATCH_QUEUE_SERIAL / NULL 串行
    dispatch_queue_t queue = dispatch_queue_create("zhouhao", NULL);
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"当前线程 = %@ -- i = %ld",[NSThread currentThread],(long)i);
        }
    });
    
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"    当前线程 = %@ -- j = %ld",[NSThread currentThread],(long)i);
        }
    });
}


Output
** Demo[6253:331370] ****主线程**** = <NSThread: 0x7fbfd3504f20>{number = 1, name = main}**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- i = 0**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- i = 1**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- i = 2**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- i = 3**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- i = 4**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- i = 5**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- i = 6**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- i = 7**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- i = 8**
** Demo[6253:331494] ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- i = 9**
** Demo[6253:331494]     ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- j = 0**
** Demo[6253:331494]     ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- j = 1**
** Demo[6253:331494]     ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- j = 2**
** Demo[6253:331494]     ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- j = 3**
** Demo[6253:331494]     ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- j = 4**
** Demo[6253:331494]     ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- j = 5**
** Demo[6253:331494]     ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- j = 6**
** Demo[6253:331494]     ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- j = 7**
** Demo[6253:331494]     ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- j = 8**
** Demo[6253:331494]     ****当前线程**** = <NSThread: 0x7fbfd34a3fd0>{number = 2, name = (null)} -- j = 9**

并行队列同步执行:任务一个一个执行,不会开辟新线程(这个一直有疑问,不知道怎么理解,有知道的请告知。)

//获取当前线程
NSLog(@"主线程 = %@",[NSThread currentThread]);

{
//并行队列同步
    dispatch_queue_t queue = dispatch_queue_create("zhouhao", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(queue, ^{
        NSLog(@"当前线程 = %@ -- 执行循序1",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"当前线程 = %@ -- 执行循序2",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"当前线程 = %@ -- 执行循序3",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"当前线程 = %@ -- 执行循序4",[NSThread currentThread]);
    });
}

Output
** Demo[6272:332352] ****主线程**** = <NSThread: 0x7fee33404790>{number = 1, name = main}**
** Demo[6272:332352] ****当前线程**** = <NSThread: 0x7fee33404790>{number = 1, name = main} -- ****执行循序****1**
** Demo[6272:332352] ****当前线程**** = <NSThread: 0x7fee33404790>{number = 1, name = main} -- ****执行循序****2**
** Demo[6272:332352] ****当前线程**** = <NSThread: 0x7fee33404790>{number = 1, name = main} -- ****执行循序****3**
** Demo[6272:332352] ****当前线程**** = <NSThread: 0x7fee33404790>{number = 1, name = main} -- ****执行循序****4**

并行队列异步执行:开辟新线程,有几个任务就开辟几个线程,任务执行是无序的。

//获取当前线程
NSLog(@"主线程 = %@",[NSThread currentThread]);
{
//并行队列异步
    dispatch_queue_t queue = dispatch_queue_create("zhouhao", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"当前线程 = %@ -- 执行循序1",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"当前线程 = %@ -- 执行循序2",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"当前线程 = %@ -- 执行循序3",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"当前线程 = %@ -- 执行循序4",[NSThread currentThread]);
    });
}

Output
** Demo[6312:333812] ****主线程**** = <NSThread: 0x7fbf4be04f70>{number = 1, name = main}**
** Demo[6312:333917] ****当前线程**** = <NSThread: 0x7fbf4bc0c3e0>{number = 2, name = (null)} -- ****执行循序****1**
** Demo[6312:333905] ****当前线程**** = <NSThread: 0x7fbf4bf8e910>{number = 3, name = (null)} -- ****执行循序****2**
** Demo[6312:333938] ****当前线程**** = <NSThread: 0x7fbf4bf90d30>{number = 5, name = (null)} -- ****执行循序****4**
** Demo[6312:333924] ****当前线程**** = <NSThread: 0x7fbf4be13f80>{number = 4, name = (null)} -- ****执行循序****3**
串行、并行队列同步任务.png
串行队列异步任务.png
并行队列异步任务.png

iOS支持多个层次的多线程编程,层次越高的抽象程度越高,使用也越方便

1、NSThread:是三种方法里面相对轻量级的,更直观地控制线程对象(优点)。但需要管理线程的生命周期、同步、加锁问题,这会导致一定的性能开销(缺点)
2、NSOperation:是基于OC实现的,NSOperation以面向对象的方式封装了需要执行的操作,不必关心线程管理、同步等问题。NSOperation是一个抽象基类,iOS提供了两种默认实现:NSInvocationOperation和NSBlockOperation,当然也可以自定义NSOperation。NSOperation是基于GCD实现,但是相对于GCD来说可控性更强,并且可以加入操作依赖。
3、GCDGCD是异步执行任务的技术之一。它更倾向于任务操作的管理,更面向过程。

如果在主队列中使用同步sync,程序会卡住(造成了死锁)。如果在非主队列的串行队列中使用同步,则不会造成卡住现象。以及下图情况也会造成卡住

串行队列嵌套--卡住.png

关于GCD的问题。

1、并行队列,顺序执行1000个任务,任务执行的时间各不相同。怎样才能使其也顺序的结束?
2、一个子线程A,执行完毕后,得到结果a,A不结束,然后返回主线程用a更新B,然后再回到A使用a执行C。怎么做?
3、

内容持续更新中。。。

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

推荐阅读更多精彩内容