源码

首页 » 归档 » 源码 » 谈谈GCD的基础(一)-ios学习从入门到精通尽在姬长信

谈谈GCD的基础(一)-ios学习从入门到精通尽在姬长信

分享最热门资讯

  • 本文为CocoaChina网友littleskyw2投稿

一.队列创建

1.系统创建的队列 -> (主队列、全局队列)

1.主队列

//一个默认的与主线程绑定的队列,称之为主队列,主线是在main()函数调用之前被创建,创建主线程的同时主队列也一起被创建,提交到主队列的blocks将会在主线程执行。主队列是串行队列(serial Queue)

1113349-fc49a2dc0d9ae170.png

2.全局队列

//一个众所周知的全局队列,随之绑定这一个优先级标志  全局队列是 并发队列(concurrent Queue)

//identifier 标识全局队列的优先级

//DISPATCH_QUEUE_PRIORITY_HIGH          高优先级    (最先添加执行的优先级)

//DISPATCH_QUEUE_PRIORITY_DEFAULT      默认优先级  (在高优先级之后添加执行)

//DISPATCH_QUEUE_PRIORITY_LOW          低优先级    (在默认优先级之后添加执行)

//DISPATCH_QUEUE_PRIORITY_BACKGROUND    后台模式    (在所有高优先级都被添加到执行队列并且系统将在后台的当前Queue执行任务才被添加并执行)

//flags 保留参数,传NULL

注意:如果global Queue不存在的情况下,该方法可能返回一个NULL对象

1113349-fd8b0daf8ec57a39.png

2.自定义队列  -> (串行队列、并发队列)

dispatch_queue_t  dispatch_queue_create(const char *_Nullable label, dispatch_queue_attr_t _Nullable attr);

 通过该API创建一个用于提交Blocks的派发队列。

参数 label:一个依附于队列的字符串标签。

         attr:预先定义的队列类型、如:DISPATCH_QUEUE_SERIAL, DISPATCH_QUEUE_CONCURRENT;如果该参数为0或NULL则为DISPATCH_QUEUE_SERIAL

注意:如果为非ARC在不需要使用时需要调用dispatch_release()释放对象,ARC不需要。

1.自定义串行队列

1113349-2b74a0bde140055a.png

2.自定义并发队列

1113349-bfa0bbddc6a8812f.png

二:任务提交方式

1.同步提交

同步的特点就是需要等待上个block执行完成,下一个block才会执行;所以同步提交到队列的任务是按照提交顺序执行的,执行结果与串行和并发无关,只与block的提交顺序有关。

注意:在同步提交时会使用当前的提交线程作为block的执行线程(同步提交未创建新的线程);如果是在主线程提交block,那么block将在主线程上执行,所以这里我使用了异步的嵌套使同步提交在子线程执行。

1.同步提交到串行队列

实现代码

1113349-596e071bc3c3e407.png

执行结果:

2017-09-21 10:28:04.247 GCD[3594:83975] ---- 0000{number = 3, name = (null)}

2017-09-21 10:28:28.288 GCD[3594:83975] ---- 1111{number = 3, name = (null)}

2017-09-21 10:28:28.289 GCD[3594:83975] ---- 6666{number = 3, name = (null)}

2017-09-21 10:28:28.289 GCD[3594:83975] ---- 提交结束 ---

2.同步提交到并发队列

将上述1中的synSerialQueue更改为synConcurrentQueue

同步提交到并发队列的执行结果与同步提交到串行队列的执行结果一直,这也再次验证了,同步提交的执行结果与串行并发无关,只与block的提交顺序有关,先提交的block先执行,最后提交的block最后执行。

3.同步嵌套提交

因为同步提交的block执行是在block提交的线程,所以blocks是在同一个线程执行,并且执行的顺序就是提交顺序。(不推荐使用嵌套)

注意:对于同步提交的target队列,如果是嵌套提交的话synSerialQueue一定不要包含synSerialQueue,否则导致程序崩溃。

实现代码:

1113349-e3178fec1075f14e.png

执行结果:

2017-09-21 11:00:57.792 GCD[4203:109238] ---- 0000{number = 3, name = (null)}

2017-09-21 11:01:23.056 GCD[4203:109238] ----  2222{number = 3, name = (null)}

2017-09-21 11:01:23.056 GCD[4203:109238] ---- 1111{number = 3, name = (null)}

2017-09-21 11:01:23.057 GCD[4203:109237] ----  3333{number = 4, name = (null)}

2017-09-21 11:01:23.057 GCD[4203:109238] ---- 6666{number = 3, name = (null)}

2017-09-21 11:01:23.057 GCD[4203:109238] ---- 提交结束 ---

以下嵌套提交会导致崩溃

1113349-44f397706a7cea14.png

1113349-e47662909865bb7c.png

2.异步提交

异步提交的特点是不需要等待,并且默认开辟子线程,提交到队列的任务会在子线程执行,所以任务提交能够很快在当前线程执行完毕,不会阻塞当前线程,但是提交的任务执行过程和提交到串行队列还是并行队列有关。

1.异步提交到串行队列

如果是提交到串行队列,那么提交的任务会按照提交顺序,依次执行。

以下代码的block提交顺序是1111 -> 4444 -> 2222

实现代码:

1113349-f54b676da61b78b8.png

执行结果

2017-09-21 10:01:19.592 GCD[3215:67942] ---- 提交结束---

2017-09-21 10:01:19.592 GCD[3215:67985] ----  1111{number = 3, name = (null)}

2017-09-21 10:01:43.880 GCD[3215:67985] ---- 3333{number = 3, name = (null)}

2017-09-21 10:02:07.983 GCD[3215:67985] ----  4444{number = 3, name = (null)}

2017-09-21 10:02:07.984 GCD[3215:67985] ---- 2222{number = 3, name = (null)}

2.异步提交到并发队列

异步提交到并发队列的任务执行顺序具有不可预测性,因为并发队列在执行异步任务时,会创建多条子线程(具体创建多少条与最大并发数有关),每个提交到并发队列的block被分配到不同的子线程执行。所以执行顺序与执行block的耗时有关,耗时较少的会先执行完毕,耗时最多的block最后执行完毕。

实现代码:

1113349-2bffe669e0c8b836.png

执行结果:

2017-09-21 10:09:18.934 GCD[3326:72607] ---- 提交结束---

2017-09-21 10:09:18.934 GCD[3326:72665] ---- 1111{number = 3, name = (null)}

2017-09-21 10:09:18.937 GCD[3326:72668] ---- 2222{number = 4, name = (null)}

2017-09-21 10:09:43.201 GCD[3326:72686] ---- 4444{number = 5, name = (null)}

2017-09-21 10:10:07.227 GCD[3326:72665] ---- 3333{number = 3, name = (null)}

三.基本使用

dispatch_once

在程序运行期间只执行一次,一定要确保onceToken变量为static否则会多次执行,一般用于单利创建。

1113349-2331e94f52143cff.png

dispatch_after

延迟操作,设置当前时间多少秒后执行,但是由于线程切换需要耗时,所以对时间要求精准的操作不建议使用这个API。

1113349-8f190b329e8371d7.png

dispatch_group

任务组操作,一般在开发中我们不会简单的给每个队列只添加一个任务;对于执行多个任务的队列,串行队列依次按照block添加的顺序执行,这样我们可以在最后的block中做处理;但是对于并发队列因为我们不能确定block的执行顺序,所以我们不能确定blocks执行完毕的处理操作应该放在那里,dispatch_group就是为了解决这样的问题而存在的;dispatch_group将提交到队列的blocks化为一个组,不管执行顺序,等组内的blocks全部执行完毕之后dispatch_group_notifyblock会进行回调。

注意:关于notify的执行线程根据自己的需要选择是在主线程,还是子线程执行。

实现代码:

1113349-13e1843ec28e8216.png

执行结果:

2017-09-20 15:55:29.083 GCD[8848:280156] --------{number = 1, name = main}

2017-09-20 15:55:29.083 GCD[8848:280277] 222222{number = 5, name = (null)}

2017-09-20 15:55:53.484 GCD[8848:280262] 333333{number = 3, name = (null)}

2017-09-20 15:56:19.596 GCD[8848:280264] 111111{number = 4, name = (null)}

2017-09-20 15:56:19.597 GCD[8848:280264] group Exeture End{number = 4, name = (null)}

dispatch_group_enter <-->dispatch_group_leave) enter<-->leave对会阻塞当前线程执行enter<-->leave对之前的blocks会和enter<-->leave对之内的操作同时执行,当enter<-->leave对之间的任务执行完毕当前线程继续执行。

注意:不建议在主线程使用, enter<-->leave一定要成对存在否者可能崩溃

实现代码:

1113349-f3c7a7bc642e278e.png

执行结果:

2017-09-20 16:58:38.894 GCD[9945:344226] 222222{number = 3, name = (null)}

2017-09-20 16:59:04.047 GCD[9945:344296] enter ----- leave{number = 4, name = (null)}

2017-09-20 16:59:04.047 GCD[9945:344296] --------{number = 4, name = (null)}

2017-09-20 16:59:28.649 GCD[9945:344225] 111111{number = 5, name = (null)}

2017-09-20 16:59:54.969 GCD[9945:344295] 333333{number = 6, name = (null)}

dispatch_group_wait阻塞当前线程,当dispatch_group的blocks全部执行完毕之后执行dispatch_group_wait之后的操作。

注意:因为阻塞线程原因不建议在主线程使用

1113349-9bd694b69847ccf6.png

2017-09-21 10:28:04.247 GCD[3594:83975] ---- 0000{number = 3, name = (null)}

2017-09-21 10:28:28.288 GCD[3594:83975] ---- 1111{number = 3, name = (null)}

2017-09-21 10:28:28.289 GCD[3594:83975] ---- 6666{number = 3, name = (null)}

2017-09-21 10:28:28.289 GCD[3594:83975] ---- 提交结束 ---

dispatch_barrier

提供一个栅栏方法,用于实现在当前队列同步执行一个block,主要用于多个异步并发任务的分段处理,可以很好的替代dispatch_group

实现代码:

1113349-42895a3ab05f5319.png

执行结果:

2017-09-21 12:01:49.955 GCD[5075:149864] ---- 任务提交结束 ----

2017-09-21 12:01:49.955 GCD[5075:149903] 222{number = 3, name = (null)}

2017-09-21 12:02:15.990 GCD[5075:149926] 111{number = 4, name = (null)}

2017-09-21 12:02:15.990 GCD[5075:149926] barrier : 333{number = 4, name = (null)}

2017-09-21 12:02:15.990 GCD[5075:149926] 444{number = 4, name = (null)}

2017-09-21 12:02:15.990 GCD[5075:149905] 555{number = 5, name = (null)}

dispatch_semaphore

我们知道多线程开发最难的就是执行顺序的控制,苹果已经给我们封装好了一些流控制的东西,像dispatch_group,等但是有时候某些场景还是需要我们自己实现对代码执行的控制,毕竟我们不希望自己写的代码自己都不知道执行顺序,dispatch_semaphore就是为了这个目的而存在的,我们可以设置一个cout来控制程序按照我们的意愿来执行。

注意:对于这样的阻塞线程的操作,最好不要放在主线程,除非特殊要求。我觉得这应该是我们用多线程开发的共识了。

代码实现:

1113349-11d829129ac44f1b.png

打印结果:

2017-09-21 11:29:23.636 GCD[4725:132344] ---- wait ----

2017-09-21 11:29:48.073 GCD[4725:132411] ---- 信号即将发送 ----

2017-09-21 11:29:48.074 GCD[4725:132344] ---- wait end ----

好了!这篇博客就写到这吧!可能对一些牛人来说这些都是比较浅的东西,但是我想还是拿来记录一下,算是一个系统的总结吧!后期还会跟进一片详细的更多高级的GCD的应用篇,最后欢迎大家多提提意见,如果有什么个人理解偏差的欢迎各路大神指正。谢谢!

ios学习入门精通尽在长信

谈谈GCD的基础(一)-ios学习从入门到精通尽在姬长信

分享最热门资讯

  • 本文为CocoaChina网友littleskyw2投稿

一.队列创建

1.系统创建的队列 -> (主队列、全局队列)

1.主队列

//一个默认的与主线程绑定的队列,称之为主队列,主线是在main()函数调用之前被创建,创建主线程的同时主队列也一起被创建,提交到主队列的blocks将会在主线程执行。主队列是串行队列(serial Queue)

1113349-fc49a2dc0d9ae170.png

2.全局队列

//一个众所周知的全局队列,随之绑定这一个优先级标志  全局队列是 并发队列(concurrent Queue)

//identifier 标识全局队列的优先级

//DISPATCH_QUEUE_PRIORITY_HIGH          高优先级    (最先添加执行的优先级)

//DISPATCH_QUEUE_PRIORITY_DEFAULT      默认优先级  (在高优先级之后添加执行)

//DISPATCH_QUEUE_PRIORITY_LOW          低优先级    (在默认优先级之后添加执行)

//DISPATCH_QUEUE_PRIORITY_BACKGROUND    后台模式    (在所有高优先级都被添加到执行队列并且系统将在后台的当前Queue执行任务才被添加并执行)

//flags 保留参数,传NULL

注意:如果global Queue不存在的情况下,该方法可能返回一个NULL对象

1113349-fd8b0daf8ec57a39.png

2.自定义队列  -> (串行队列、并发队列)

dispatch_queue_t  dispatch_queue_create(const char *_Nullable label, dispatch_queue_attr_t _Nullable attr);

 通过该API创建一个用于提交Blocks的派发队列。

参数 label:一个依附于队列的字符串标签。

         attr:预先定义的队列类型、如:DISPATCH_QUEUE_SERIAL, DISPATCH_QUEUE_CONCURRENT;如果该参数为0或NULL则为DISPATCH_QUEUE_SERIAL

注意:如果为非ARC在不需要使用时需要调用dispatch_release()释放对象,ARC不需要。

1.自定义串行队列

1113349-2b74a0bde140055a.png

2.自定义并发队列

1113349-bfa0bbddc6a8812f.png

二:任务提交方式

1.同步提交

同步的特点就是需要等待上个block执行完成,下一个block才会执行;所以同步提交到队列的任务是按照提交顺序执行的,执行结果与串行和并发无关,只与block的提交顺序有关。

注意:在同步提交时会使用当前的提交线程作为block的执行线程(同步提交未创建新的线程);如果是在主线程提交block,那么block将在主线程上执行,所以这里我使用了异步的嵌套使同步提交在子线程执行。

1.同步提交到串行队列

实现代码

1113349-596e071bc3c3e407.png

执行结果:

2017-09-21 10:28:04.247 GCD[3594:83975] ---- 0000{number = 3, name = (null)}

2017-09-21 10:28:28.288 GCD[3594:83975] ---- 1111{number = 3, name = (null)}

2017-09-21 10:28:28.289 GCD[3594:83975] ---- 6666{number = 3, name = (null)}

2017-09-21 10:28:28.289 GCD[3594:83975] ---- 提交结束 ---

2.同步提交到并发队列

将上述1中的synSerialQueue更改为synConcurrentQueue

同步提交到并发队列的执行结果与同步提交到串行队列的执行结果一直,这也再次验证了,同步提交的执行结果与串行并发无关,只与block的提交顺序有关,先提交的block先执行,最后提交的block最后执行。

3.同步嵌套提交

因为同步提交的block执行是在block提交的线程,所以blocks是在同一个线程执行,并且执行的顺序就是提交顺序。(不推荐使用嵌套)

注意:对于同步提交的target队列,如果是嵌套提交的话synSerialQueue一定不要包含synSerialQueue,否则导致程序崩溃。

实现代码:

1113349-e3178fec1075f14e.png

执行结果:

2017-09-21 11:00:57.792 GCD[4203:109238] ---- 0000{number = 3, name = (null)}

2017-09-21 11:01:23.056 GCD[4203:109238] ----  2222{number = 3, name = (null)}

2017-09-21 11:01:23.056 GCD[4203:109238] ---- 1111{number = 3, name = (null)}

2017-09-21 11:01:23.057 GCD[4203:109237] ----  3333{number = 4, name = (null)}

2017-09-21 11:01:23.057 GCD[4203:109238] ---- 6666{number = 3, name = (null)}

2017-09-21 11:01:23.057 GCD[4203:109238] ---- 提交结束 ---

以下嵌套提交会导致崩溃

1113349-44f397706a7cea14.png

1113349-e47662909865bb7c.png

2.异步提交

异步提交的特点是不需要等待,并且默认开辟子线程,提交到队列的任务会在子线程执行,所以任务提交能够很快在当前线程执行完毕,不会阻塞当前线程,但是提交的任务执行过程和提交到串行队列还是并行队列有关。

1.异步提交到串行队列

如果是提交到串行队列,那么提交的任务会按照提交顺序,依次执行。

以下代码的block提交顺序是1111 -> 4444 -> 2222

实现代码:

1113349-f54b676da61b78b8.png

执行结果

2017-09-21 10:01:19.592 GCD[3215:67942] ---- 提交结束---

2017-09-21 10:01:19.592 GCD[3215:67985] ----  1111{number = 3, name = (null)}

2017-09-21 10:01:43.880 GCD[3215:67985] ---- 3333{number = 3, name = (null)}

2017-09-21 10:02:07.983 GCD[3215:67985] ----  4444{number = 3, name = (null)}

2017-09-21 10:02:07.984 GCD[3215:67985] ---- 2222{number = 3, name = (null)}

2.异步提交到并发队列

异步提交到并发队列的任务执行顺序具有不可预测性,因为并发队列在执行异步任务时,会创建多条子线程(具体创建多少条与最大并发数有关),每个提交到并发队列的block被分配到不同的子线程执行。所以执行顺序与执行block的耗时有关,耗时较少的会先执行完毕,耗时最多的block最后执行完毕。

实现代码:

1113349-2bffe669e0c8b836.png

执行结果:

2017-09-21 10:09:18.934 GCD[3326:72607] ---- 提交结束---

2017-09-21 10:09:18.934 GCD[3326:72665] ---- 1111{number = 3, name = (null)}

2017-09-21 10:09:18.937 GCD[3326:72668] ---- 2222{number = 4, name = (null)}

2017-09-21 10:09:43.201 GCD[3326:72686] ---- 4444{number = 5, name = (null)}

2017-09-21 10:10:07.227 GCD[3326:72665] ---- 3333{number = 3, name = (null)}

三.基本使用

dispatch_once

在程序运行期间只执行一次,一定要确保onceToken变量为static否则会多次执行,一般用于单利创建。

1113349-2331e94f52143cff.png

dispatch_after

延迟操作,设置当前时间多少秒后执行,但是由于线程切换需要耗时,所以对时间要求精准的操作不建议使用这个API。

1113349-8f190b329e8371d7.png

dispatch_group

任务组操作,一般在开发中我们不会简单的给每个队列只添加一个任务;对于执行多个任务的队列,串行队列依次按照block添加的顺序执行,这样我们可以在最后的block中做处理;但是对于并发队列因为我们不能确定block的执行顺序,所以我们不能确定blocks执行完毕的处理操作应该放在那里,dispatch_group就是为了解决这样的问题而存在的;dispatch_group将提交到队列的blocks化为一个组,不管执行顺序,等组内的blocks全部执行完毕之后dispatch_group_notifyblock会进行回调。

注意:关于notify的执行线程根据自己的需要选择是在主线程,还是子线程执行。

实现代码:

1113349-13e1843ec28e8216.png

执行结果:

2017-09-20 15:55:29.083 GCD[8848:280156] --------{number = 1, name = main}

2017-09-20 15:55:29.083 GCD[8848:280277] 222222{number = 5, name = (null)}

2017-09-20 15:55:53.484 GCD[8848:280262] 333333{number = 3, name = (null)}

2017-09-20 15:56:19.596 GCD[8848:280264] 111111{number = 4, name = (null)}

2017-09-20 15:56:19.597 GCD[8848:280264] group Exeture End{number = 4, name = (null)}

dispatch_group_enter <-->dispatch_group_leave) enter<-->leave对会阻塞当前线程执行enter<-->leave对之前的blocks会和enter<-->leave对之内的操作同时执行,当enter<-->leave对之间的任务执行完毕当前线程继续执行。

注意:不建议在主线程使用, enter<-->leave一定要成对存在否者可能崩溃

实现代码:

1113349-f3c7a7bc642e278e.png

执行结果:

2017-09-20 16:58:38.894 GCD[9945:344226] 222222{number = 3, name = (null)}

2017-09-20 16:59:04.047 GCD[9945:344296] enter ----- leave{number = 4, name = (null)}

2017-09-20 16:59:04.047 GCD[9945:344296] --------{number = 4, name = (null)}

2017-09-20 16:59:28.649 GCD[9945:344225] 111111{number = 5, name = (null)}

2017-09-20 16:59:54.969 GCD[9945:344295] 333333{number = 6, name = (null)}

dispatch_group_wait阻塞当前线程,当dispatch_group的blocks全部执行完毕之后执行dispatch_group_wait之后的操作。

注意:因为阻塞线程原因不建议在主线程使用

1113349-9bd694b69847ccf6.png

2017-09-21 10:28:04.247 GCD[3594:83975] ---- 0000{number = 3, name = (null)}

2017-09-21 10:28:28.288 GCD[3594:83975] ---- 1111{number = 3, name = (null)}

2017-09-21 10:28:28.289 GCD[3594:83975] ---- 6666{number = 3, name = (null)}

2017-09-21 10:28:28.289 GCD[3594:83975] ---- 提交结束 ---

dispatch_barrier

提供一个栅栏方法,用于实现在当前队列同步执行一个block,主要用于多个异步并发任务的分段处理,可以很好的替代dispatch_group

实现代码:

1113349-42895a3ab05f5319.png

执行结果:

2017-09-21 12:01:49.955 GCD[5075:149864] ---- 任务提交结束 ----

2017-09-21 12:01:49.955 GCD[5075:149903] 222{number = 3, name = (null)}

2017-09-21 12:02:15.990 GCD[5075:149926] 111{number = 4, name = (null)}

2017-09-21 12:02:15.990 GCD[5075:149926] barrier : 333{number = 4, name = (null)}

2017-09-21 12:02:15.990 GCD[5075:149926] 444{number = 4, name = (null)}

2017-09-21 12:02:15.990 GCD[5075:149905] 555{number = 5, name = (null)}

dispatch_semaphore

我们知道多线程开发最难的就是执行顺序的控制,苹果已经给我们封装好了一些流控制的东西,像dispatch_group,等但是有时候某些场景还是需要我们自己实现对代码执行的控制,毕竟我们不希望自己写的代码自己都不知道执行顺序,dispatch_semaphore就是为了这个目的而存在的,我们可以设置一个cout来控制程序按照我们的意愿来执行。

注意:对于这样的阻塞线程的操作,最好不要放在主线程,除非特殊要求。我觉得这应该是我们用多线程开发的共识了。

代码实现:

1113349-11d829129ac44f1b.png

打印结果:

2017-09-21 11:29:23.636 GCD[4725:132344] ---- wait ----

2017-09-21 11:29:48.073 GCD[4725:132411] ---- 信号即将发送 ----

2017-09-21 11:29:48.074 GCD[4725:132344] ---- wait end ----

好了!这篇博客就写到这吧!可能对一些牛人来说这些都是比较浅的东西,但是我想还是拿来记录一下,算是一个系统的总结吧!后期还会跟进一片详细的更多高级的GCD的应用篇,最后欢迎大家多提提意见,如果有什么个人理解偏差的欢迎各路大神指正。谢谢!

ios学习入门精通尽在长信

(0)

本文由 姬長信 创作,文章地址:https://blog.isoyu.com/archives/20491.html
采用知识共享署名4.0 国际许可协议进行许可。除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。最后编辑时间为:9 月 24, 2017 at 04:54 下午

关键词:, , , ,

热评文章

发表回复

[必填]

我是人?

提交后请等待三秒以免造成未提交成功和重复