老生常谈-GCD线程机制总结

来源:互联网 发布:js中的format函数 编辑:程序博客网 时间:2024/05/29 03:43

使用GCD多线程开发曾经就像会使用block一样,是显得那么的高大上。但如今,在iOS开发中对其的使用早已是司空见惯,家常便饭了,dispatch_async、dispatch_get_main_queue()、dispatch_get_global_queue()、dispatch_queue_create()这些函数早就是在项目里用烂的语句了。不过使用的人是很多,但GCD似乎并不像其显示出的那样“平易近人”,其中涉及到的任务、队列、线程等等一些基本的概念,还总是被隔三差五的在各种文章中被反复提起,反复分析,因为这些概念确实是挺“乱”的,糊涂的人仍然是大多数。最近我又在论坛里看到了不少GCD相关的问题,大家的回答与讨论,对GCD的理解,确实也可以称得上各说各话,百家争鸣了。于是我也想写篇博客,谈谈我对GCD的一些基本概念的理解与总结。

一、一些概念

任务:

简单理解,

dispatch_async(dispatch_queue_t queue, dispatch_block_t block)
dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block)
每次调用这两个函数,就会往目标队列中添加一个任务(block中定义的就是一个任务),任务都是被加入到目标队列队尾的.

示例:

    NSLog(@"任务1:语句1");    NSLog(@"任务1:语句2");    NSLog(@"任务1:语句3");    dispatch_queue_t concurrentQueue = dispatch_queue_create("ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);    dispatch_async(concurrentQueue, ^{        NSLog(@"任务2:语句1");        NSLog(@"任务2:语句2");        dispatch_sync(concurrentQueue, ^{            NSLog(@"任务3:语句1");            NSLog(@"任务3:语句2");        });        dispatch_async(dispatch_get_main_queue(), ^{            NSLog(@"任务4:语句1");            NSLog(@"任务4:语句2");        });        NSLog(@"任务2:语句3");    });    NSLog(@"任务1:语句4");
现在主队列中有任务1和任务4,concurrentQueue队列中有任务2和任务3

并行队列与串行队列:

并行队列中的任务执行没有先后关系,可同时进行

串行队列中任务要按在队列中顺序,以任务为单位,先后执行

同步执行与异步执行:

同步执行一个任务会阻塞当前所在线程

异步执行一个任务不会阻塞当前所在线程

二、关于线程的创建

1.使用dispatch_sync同步执行,那么无论使用的串行还是并行队列,系统都不会创建新的线程(任务都会在当前线程中执行)

2.dispatch_async(异步)+串行队列:针对每个串行队列只创建一个线程(以队列为单位)

3.dispatch_async(异步)+并行队列:系统会根据当前状态,有数量控制的为每个任务创建或分配一个线程(以任务为单位)

示例:

1、使用dispatch_sync同步执行

如果你使用的是dispatch_sync同步执行,那么无论使用的串行还是并行队列,系统都不会创建新的线程(任务都会在当前线程中执行)

    //主线程    NSLog(@"1:%@",[NSThread currentThread]);    dispatch_sync(dispatch_get_global_queue(0, 0), ^{        //主线程        NSLog(@"2:%@",[NSThread currentThread]);    });        dispatch_queue_t serialQueue = dispatch_queue_create("SerialQueue", NULL);    dispatch_sync(serialQueue, ^{        //主线程        NSLog(@"3:%@",[NSThread currentThread]);    });        dispatch_queue_t concurrentQueue = dispatch_queue_create("ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);    dispatch_sync(concurrentQueue, ^{        //主线程        NSLog(@"4:%@",[NSThread currentThread]);    });        dispatch_sync(dispatch_get_global_queue(0, 0), ^{        //主线程        NSLog(@"5:%@",[NSThread currentThread]);    });        dispatch_async(dispatch_get_global_queue(0, 0), ^{        //使用了async,创建一子线程 402944        NSLog(@"6:%@",[NSThread currentThread]);        dispatch_sync(dispatch_get_global_queue(0, 0), ^{            //没有再启一个线程,仍然在在子线程402944            NSLog(@"7:%@",[NSThread currentThread]);        });        //子线程402944        NSLog(@"8:%@",[NSThread currentThread]);    });



2、使用dispatch_async异步执行

2.1.dispatch_async+串行队列:针对每个串行队列只创建一个线程

    //主线程    NSLog(@"1:%@",[NSThread currentThread]);        dispatch_queue_t serialQueue1 = dispatch_queue_create("SerialQueue1", NULL);    dispatch_async(serialQueue1, ^{        //针对serialQueue1创建的线程412525        NSLog(@"2:%@",[NSThread currentThread]);    });    dispatch_async(serialQueue1, ^{        //针对serialQueue1创建的线程412525        NSLog(@"3:%@",[NSThread currentThread]);    });    dispatch_async(serialQueue1, ^{        //针对serialQueue1创建的线程412525        NSLog(@"4:%@",[NSThread currentThread]);    });        dispatch_queue_t serialQueue2 = dispatch_queue_create("SerialQueue2", NULL);    dispatch_async(serialQueue2, ^{        //针对serialQueue2创建的线程412507        NSLog(@"5:%@",[NSThread currentThread]);    });    dispatch_async(serialQueue2, ^{        //针对serialQueue2创建的线程412507        NSLog(@"6:%@",[NSThread currentThread]);    });    dispatch_async(serialQueue2, ^{        //针对serialQueue2创建的线程412507        NSLog(@"7:%@",[NSThread currentThread]);    });        dispatch_async(dispatch_get_main_queue(), ^{        //主线程,dispatch_get_main_queue是串行队列        NSLog(@"8:%@",[NSThread currentThread]);    });    dispatch_async(dispatch_get_main_queue(), ^{        //主线程,dispatch_get_main_queue是串行队列        NSLog(@"9:%@",[NSThread currentThread]);    });

2.2.dispatch_async+并行队列

    dispatch_queue_t concurrentQueue = dispatch_queue_create("ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);    //主线程    NSLog(@"1:%@",[NSThread currentThread]);    dispatch_async(concurrentQueue, ^{        //线程1058771        NSLog(@"2:%@",[NSThread currentThread]);    });    dispatch_async(concurrentQueue, ^{        //线程1058769        NSLog(@"3:%@",[NSThread currentThread]);    });    dispatch_async(concurrentQueue, ^{        //线程1058769        NSLog(@"4:%@",[NSThread currentThread]);    });    dispatch_async(concurrentQueue, ^{        //线程1058771        NSLog(@"5:%@",[NSThread currentThread]);    });    dispatch_async(concurrentQueue, ^{        //线程1058769        NSLog(@"6:%@",[NSThread currentThread]);    });    dispatch_async(concurrentQueue, ^{        //线程1058771        NSLog(@"7:%@",[NSThread currentThread]);    });

针对每个任务,系统会根据当前状态,为每个任务创建或分配一个线程,线程不会无限的被增加,会复用线程。

dispatch_queue_t concurrentQueue = dispatch_queue_create("ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);    dispatch_async(concurrentQueue, ^{        NSLog(@"1:%@",[NSThread currentThread]);        dispatch_async(concurrentQueue, ^{            NSLog(@"2:%@",[NSThread currentThread]);            dispatch_async(concurrentQueue, ^{                NSLog(@"3:%@",[NSThread currentThread]);                dispatch_async(concurrentQueue, ^{                    NSLog(@"4:%@",[NSThread currentThread]);                    dispatch_async(concurrentQueue, ^{                        NSLog(@"5:%@",[NSThread currentThread]);                        dispatch_async(concurrentQueue, ^{                            NSLog(@"6:%@",[NSThread currentThread]);                        });                    });                });            });        });    });

这个例子同样可得出上面的结论

三、GCD多线程代码示例分析

下面就综合利用上文所介绍的概念内容,通过对一些GCD示例代码的分析,尝试找到分析、理解GCD多线程代码的一套方法

    NSLog(@"任务1:语句1");    dispatch_sync(dispatch_get_main_queue(), ^{        NSLog(@"任务2:语句1");    });    NSLog(@"任务1:语句2");

任务2被放到了主队列队尾,也就是说任务1中的所有语句执行完成后,才能执行任务2。但是sync会阻塞当前线程直到任务2完成,任务1中的语句2就永远得不到执行。所以程序就陷入了僵局,运行时崩溃。

    NSLog(@"任务1:语句1");    dispatch_queue_t serialQueue = dispatch_queue_create("SerialQueue", NULL);    dispatch_async(serialQueue, ^{        //dispatch_async + 串行队列:创建了一个子线程        NSLog(@"任务2:语句1");        dispatch_sync(dispatch_get_main_queue(), ^{//阻塞的是当前子线程            NSLog(@"任务3:语句1");        });        NSLog(@"任务2:语句2");    });    NSLog(@"任务1:语句2");
和上面的例子有点儿像,但这次程序顺利执行。任务3被放到了主队列队尾,先执行完任务1的语句2后才能执行任务3。但是这次sync阻塞的不是主线程(而是子线程),没有影响到任务1语句2的执行,程序并未陷入僵局。

    NSLog(@"任务1:语句1");    dispatch_queue_t serialQueue = dispatch_queue_create("SerialQueue", NULL);    dispatch_sync(serialQueue, ^{        NSLog(@"任务2:语句1");    });    NSLog(@"任务1:语句2");

程序顺利执行,sync虽然仍然会阻塞主线程,但是任务2是被加到了serialQueue队列中,与任务1并无执行顺序上的关联,所以程序可正常运行。

    dispatch_queue_t serialQueue = dispatch_queue_create("SerialQueue", NULL);    NSLog(@"任务1:语句1");    dispatch_async(serialQueue, ^{        //dispatch_async + 串行队列:创建了一个子线程        NSLog(@"任务2:语句1");        dispatch_async(serialQueue, ^{            NSLog(@"任务3:语句1");        });        dispatch_async(serialQueue, ^{            NSLog(@"任务4:语句1");        });        NSLog(@"任务2:语句2");    });    NSLog(@"任务1:语句2");
正常运行,任务3和任务4被加到了串行队列serialQueue的任务2之后,但因为是异步执行,并不会阻塞线程,没有影响到任务2的执行,所以运行正常。同时,这个例子也证明了,任务都是被加到队尾的,虽然任务3和任务4是异步执行的,但因为它们处于的是串行队列,且在队尾,所以执行的顺序一定是任务2->任务3->任务4

    dispatch_queue_t concurrentQueue = dispatch_queue_create("ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);    NSLog(@"任务1:语句1");    dispatch_async(concurrentQueue, ^{        NSLog(@"任务2:语句1");        dispatch_sync(concurrentQueue, ^{            NSLog(@"任务3:语句1");        });        NSLog(@"任务2:语句2");    });    NSLog(@"任务1:语句2");
程序顺利执行,虽然任务3被加到了concurrentQueue队列的任务2后面,sync也会阻塞主子线程,但是concurrentQueue是并行队列,任务2与任务3并没有执行上的先后顺序,所以程序不会陷入僵局

        dispatch_queue_t serialQueue = dispatch_queue_create("SerialQueue", NULL);        NSLog(@"任务1:语句1");        dispatch_async(serialQueue, ^{            //dispatch_async + 串行队列:创建了一个子线程            NSLog(@"任务2:语句1");            dispatch_sync(serialQueue, ^{                NSLog(@"任务3:语句1");            });            NSLog(@"任务2:语句2");        });        NSLog(@"任务1:语句2");

程序崩溃,任务3被放到了serialQueue队列任务2后面,sync阻塞了子线程直到任务3完成,此时任务2:语句2与任务3就陷入了僵局


这些例子还能写很多,但是无论多么复杂,但只要明确以下几个问题(任务+队列+线程),问题就都会迎刃而解了:

1.划分出任务

2.明确任务所属队列

3.明确任务所属队列特点(串行还是并行)

4.明确任务执行所在线程

5.考虑好async(sync)对线程的影响

6.关键,看看线程层面的执行(阻不阻塞)是否对队列任务的调度(串行或并行)造成了影响



0 0
原创粉丝点击