GCD是基于C的API,它是libdispatch的的市场名称。而libdispatch作为Apple公司的一个库,为并发代码在多核硬件(跑IOS或者OS X)上执行提供有力支持。

那么我们为什么要用GCD技术呢?

1.GCD能够推迟昂贵的计算任务,并在后台运行它们来改善你的应用的性能。

2.GCD提供一个易于使用的并发模型而不仅仅是锁和线程。以帮助我们避开并发陷阱。

3.GCD具有在常见模式(比如单例)上用更高性能的原语优化你的代码的潜在能力。

4.GCD旨在替换NSTread等线程技术。

5.GCD可充分利用设备的多核。

6.GCD可自动管理线程的生命周期。

说了这些GCD的优点,那么在实际开发中,如何使用GCD来更好满足我们的需求呢?

一、Synchronous&Asynchronous 同步&异步

1.Synchronous同步:同步任务的执行的方式:在当前线程中执行,必须等待当前语句执行完毕,才会执行下一条语句。

来看下同步的代码:

  1. //同步的打印顺序
  2. -(void)syncTask{
  3. NSLog(@"begin");
  4. //GCD的同步方法
  5. dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
  6. //任务中要执行的代码
  7. [NSThread sleepForTimeInterval:2.0];
  8. NSLog(@"%@",[NSThread currentThread]);
  9. });
  10. NSLog(@"end");
  11. }

来看打印出来的:

2019-03-29 12:00:54.993042+0800 wftest[5191:88411] begin

2019-03-29 12:00:56.994525+0800 wftest[5191:88411] <NSThread: 0x600000ff5380>{number = 1, name = main}

2019-03-29 12:00:56.994799+0800 wftest[5191:88411] end

可以看到,即使线程休眠了2秒,他依然会按照顺序执行,等代码块内的代码执行完毕后,才会执行end.

接着我们再来看异步,不在当前线程中执行,不用等当前语句执行完毕,就可以执行下一条语句

来看代码:

  1. //异步顺序
  2. -(void)asyncTask{
  3. //异步不会在当前线程执行,首先需要开辟新的线程,而开辟新的线程也需要一定的时间
  4. NSLog(@"begin");
  5. dispatch_async(dispatch_get_global_queue(0, 0), ^{
  6. NSLog(@"%@",[NSThread currentThread]);
  7. });
  8. NSLog(@"end");
  9. }

  我们来看下打印的情况:

2019-03-29 16:06:34.600530+0800 wftest[1336:19777] begin

2019-03-29 16:06:34.600763+0800 wftest[1336:19777] end

2019-03-29 16:06:34.600892+0800 wftest[1336:20090] <NSThread: 0x600000acc600>{number = 3, name = (null)}

可以看到,打印出来begin后,直接打印出了end.然后才执行了异步块里的代码。

接下来,我们来看看串行队列Serial queues,和并行队列(并发队列)Concurrent queues.

1.串行队列的特点:

以先进先出的方式执行,顺序调度队列中的任务执行。

无论队列中的任务函数是同步还是异步,都会等待前一个任务执行完成后,再调度后面的任务。

我们先来看看,串行队列的同步代码:

  1. //串行队列同步函数(在一个线程中执行,注意,GCD的是C语言API,不要和OC弄混)
  2. -(void)serialSync{
  3. //这里有两个参数,第一个参数的标识符,一般为公司域名倒写,第二个参数队列类型,DISPATCH_QUEUE_SERIAL串行,DISPATCH_QUEUE_CONCURRENT为并发队列
  4. dispatch_queue_t serialQueue = dispatch_queue_create("com.feng", DISPATCH_QUEUE_SERIAL);
  5. //创建任务
  6. void (^task1) (void) = ^(){
  7. NSLog(@"task1---%@",[NSThread currentThread]);
  8. };
  9.  
  10. void (^task2) (void) = ^(){
  11. NSLog(@"task2 -- %@",[NSThread currentThread]);
  12. };
  13.  
  14. void (^task3) (void) = ^(){
  15. NSLog(@"task3 -- %@",[NSThread currentThread]);
  16. };
  17.  
  18. //添加任务到队列,同步执行方法
  19. dispatch_sync(serialQueue, task1);
  20. dispatch_sync(serialQueue, task2);
  21. dispatch_sync(serialQueue, task3);
  22. }

  然后来看下NSLog打印的东西:

2019-03-29 17:08:10.362074+0800 wftest[2989:50297] task1---<NSThread: 0x600002009340>{number = 1, name = main}

2019-03-29 17:08:10.364550+0800 wftest[2989:50297] task2 --  <NSThread: 0x600002009340>{number = 1, name = main}

2019-03-29 17:08:10.365860+0800 wftest[2989:50297] task3  -- <NSThread: 0x600002009340>{number = 1, name = main}

可以看到task1,taks2,task3是完全按照顺序执行的。

再来看串行队列的异步方法:

先来看代码

  1. //串行队列异步函数
  2. -(void)serialAsync{
  3. //创建一个串行队列
  4. dispatch_queue_t serialQuene = dispatch_queue_create("com.feng", DISPATCH_QUEUE_SERIAL);
  5. //2.创建任务
  6. void (^task1)(void) = ^(){
  7. NSLog(@"task1 --- %@",[NSThread currentThread]);
  8. };
  9. void (^task2)(void) = ^(){
  10. NSLog(@"task 2-- %@",[NSThread currentThread]);
  11. };
  12. void (^task3)(void) = ^(){
  13. NSLog(@"task 3 -- %@",[NSThread currentThread]);
  14. };
  15.  
  16. //3.添加任务队列
  17. dispatch_async(serialQuene, task1);
  18. dispatch_async(serialQuene, task2);
  19. dispatch_async(serialQuene, task3);
  20. }

 来看下打印结果:

  1. 2019-03-30 14:27:21.730761+0800 wftest[4929:84017] 主线程 -- <NSThread: 0x600000650540>{number = 1, name = main}
  2. 2019-03-30 14:27:21.731252+0800 wftest[4929:84090] task1 --- <NSThread: 0x600000638340>{number = 3, name = (null)}
  3. 2019-03-30 14:27:21.731536+0800 wftest[4929:84090] task 2-- <NSThread: 0x600000638340>{number = 3, name = (null)}
  4. 2019-03-30 14:27:21.731691+0800 wftest[4929:84090] task 3 -- <NSThread: 0x600000638340>{number = 3, name = (null)}

  

 

可以看到,串行队列异步执行,仍然按顺序执行的。也就是说,只要是串行队列,无论是异步,还是同步函数,都是按顺序执行的。

2.看完串行队列,我们来看并发(并行)队列。

并发队列的特点:

1.以先进先出的方法,并发调度队列中的任务的执行。

2.如果是并发队列的同步执行,就会等先被调度的任务执行完毕后,再执行下一个任务。

3.如果是并发队列的异步执行,同时底层线程池有可用的线程资源,会在新的任务调度后,调度下一个任务。

也就是说,先加进来的任务会先被执行,但不用等他执行完毕,就可以接着调度下一个任务。

那么,我们先来看并发队列的同步任务的代码:

  1. //并发队列同步函数
  2. -(void)concurrentSync{
  3. //1.创建并发队列
  4. dispatch_queue_t concurrentQueue = dispatch_queue_create("com.feng", DISPATCH_QUEUE_CONCURRENT);
  5.  
  6. //2.创建任务
  7. void (^task1) (void) = ^(){
  8. NSLog(@"task1 -- %@",[NSThread currentThread]);
  9. };
  10. void (^task2) (void) = ^(){
  11. NSLog(@"task2 -- %@",[NSThread currentThread]);
  12. };
  13. void (^task3) (void) = ^(){
  14. NSLog(@"task3 -- %@",[NSThread currentThread]);
  15. };
  16.  
  17. //3.添加同步任务到并发队列
  18. dispatch_sync(concurrentQueue, task1);
  19. dispatch_sync(concurrentQueue, task2);
  20. dispatch_sync(concurrentQueue, task3);
  21. }

  再来看打印情况:

  1. 2019-03-30 14:01:51.358796+0800 wftest[4073:69627] 主线程 -- <NSThread: 0x600001bdcd00>{number = 1, name = main}
  2. 2019-03-30 14:01:51.359220+0800 wftest[4073:69627] task1 -- <NSThread: 0x600001bdcd00>{number = 1, name = main}
  3. 2019-03-30 14:01:51.359668+0800 wftest[4073:69627] task2 -- <NSThread: 0x600001bdcd00>{number = 1, name = main}
  4. 2019-03-30 14:01:51.360195+0800 wftest[4073:69627] task3 -- <NSThread: 0x600001bdcd00>{number = 1, name = main}

  

  

  

可以看到,虽然是并发队列,但因为是同步任务,所以也是按顺序执行的。因为是同步任务,所以就在当前线程,主线程中执行。异步任务则会在子线程中执行。

(可以简单总结:串行 ,要等待上个任务执行完毕,才执行下个任务,所以会在同一个线程中执行。 并行:不用等上个任务执行完毕,就可以执行下个任务。同步:在当前线程中执行,不会开辟子线程。异步:在子线程中执行(这是指串行和并行队列。后面说的主队列异步,也是在主线程中执行))。

 

再来看并发队列的异步执行任务:

  1. //并发队列的异步执行
  2. -(void)concurrentAsyn{
  3. //1.创建队列
  4. dispatch_queue_t concurrentQueue = dispatch_queue_create("com.feng", DISPATCH_QUEUE_CONCURRENT);
  5. //2.创建任务
  6. void(^task1) (void) = ^(){
  7. NSLog(@"task1 -- %@",[NSThread currentThread]);
  8. };
  9. void(^task2) (void) = ^(){
  10. NSLog(@"task2 -- %@",[NSThread currentThread]);
  11. };
  12. void(^task3) (void) = ^(){
  13. NSLog(@"task3 -- %@",[NSThread currentThread]);
  14. };
  15. //3.把任务添加到队列中去
  16. dispatch_async(concurrentQueue, task1);
  17. dispatch_async(concurrentQueue, task2);
  18. dispatch_async(concurrentQueue, task3);
  19. }

  来看打印情况:

  1. 2019-03-30 14:02:45.003384+0800 wftest[4112:70392] 主线程 -- <NSThread: 0x600000ba8c40>{number = 1, name = main}
  2. 2019-03-30 14:02:45.005834+0800 wftest[4112:70450] task2 -- <NSThread: 0x600000bf7840>{number = 4, name = (null)}
  3. 2019-03-30 14:02:45.005834+0800 wftest[4112:70449] task1 -- <NSThread: 0x600000bcc380>{number = 3, name = (null)}
  4. 2019-03-30 14:02:45.005838+0800 wftest[4112:70454] task3 -- <NSThread: 0x600000bcc480>{number = 5, name = (null)}

  可以看到,异步任务另外开辟了子线程。可以看到打印顺序发生了变化。

接着,我们来看全局队列。

全局队列的工作表现和并发队列一致。

但是全局队列是否就是并发队列呢?不是的。我们来看下他们的区别:

1.全局队列没有名称,无论是MRC&ARC都不用考虑释放,所以在日常开发中,建议使用全局队列。

2.并发队列:有名字,和NSThread的name属性作用类似,如果你在MRC的开发中,则需要使用dispatch_releas(q)来释放对应的对象。

那么并发队列在什么时候使用呢?在你开发第三方的框架的时候,则需要使用并发队列了。这样可以避开和使用你的开发框架的程序员弄混队列。

咱们先来看下全局队列的同步任务。(日常开发中几乎用不到。)

  1. //全局队列的同步任务
  2. -(void)globalSync{
  3. NSLog(@"begin");
  4. //1.创建全局队列
  5. dispatch_queue_t gloabalQueue = dispatch_get_global_queue(0, 0);
  6. //2.创建任务
  7. void(^task1) (void) = ^(){
  8. NSLog(@"task1 -- %@",[NSThread currentThread]);
  9. };
  10. void(^task2) (void) = ^(){
  11. NSLog(@"task2 -- %@",[NSThread currentThread]);
  12. };
  13. void(^task3) (void) = ^(){
  14. NSLog(@"task3 -- %@",[NSThread currentThread]);
  15. };
  16. //3.加入任务到队列中执行
  17. dispatch_sync(gloabalQueue, task1);
  18. dispatch_sync(gloabalQueue, task2);
  19. dispatch_sync(gloabalQueue, task3);
  20. NSLog(@"end");
  21. }

  来看打印结果:

  1. 2019-03-30 14:35:08.160967+0800 wftest[5189:88230] 主线程 -- <NSThread: 0x600000684300>{number = 1, name = main}
  2. 2019-03-30 14:35:08.161223+0800 wftest[5189:88230] begin
  3. 2019-03-30 14:35:08.161470+0800 wftest[5189:88230] task1 -- <NSThread: 0x600000684300>{number = 1, name = main}
  4. 2019-03-30 14:35:08.161639+0800 wftest[5189:88230] task2 -- <NSThread: 0x600000684300>{number = 1, name = main}
  5. 2019-03-30 14:35:08.161773+0800 wftest[5189:88230] task3 -- <NSThread: 0x600000684300>{number = 1, name = main}
  6. 2019-03-30 14:35:08.161891+0800 wftest[5189:88230] end

  可以看到,按熟悉执行,同步的,都是当前线程中执行的。

再来看全局队列的异步任务,他是在子线程池上执行的,每个任务都有一个自己的线程,前提是线程池里有线程资源,底层有一个线程重用机制的。看下代码:

  1. 2019-03-30 14:39:06.429812+0800 wftest[5330:90708] 主线程 -- <NSThread: 0x600003606580>{number = 1, name = main}
  2. 2019-03-30 14:39:06.430060+0800 wftest[5330:90708] begin
  3. 2019-03-30 14:39:06.430228+0800 wftest[5330:90708] end
  4. 2019-03-30 14:39:06.430381+0800 wftest[5330:90762] task1 -- <NSThread: 0x600003660a40>{number = 3, name = (null)}
  5. 2019-03-30 14:39:06.430416+0800 wftest[5330:90763] task3 -- <NSThread: 0x600003660a00>{number = 4, name = (null)}
  6. 2019-03-30 14:39:06.430422+0800 wftest[5330:90760] task2 -- <NSThread: 0x600003660ec0>{number = 5, name = (null)}

  可以看到,每个任务都有自己的独立的线程。

有点累,一会再来看看主队列。

主队列的特点:

1.专门用来在主线程上调度任务的队列。

2.不会开启子线程

3.以先进先出的方式,在主线程空闲的时候才会调度主队列中的任务在主线程中执行。

4.如果当前主线程中有任务在执行,那么无论主队列中添加了什么任务,都不会被调度。

主队列是负责在主线程中调度任务的。

会随着程序启动一起创建。

对于我们程序员来说,主队列只需要获取,不需要创建。

那么我们来看下主队列的异步任务的代码:

  1. //主队列的异步任务
  2. -(void)mainAsync{
  3. NSLog(@"begin");
  4.  
  5. //1.创建主队列
  6. dispatch_queue_main_t mainAsync = dispatch_get_main_queue();
  7.  
  8. //2.创建任务
  9. void(^task1) (void) = ^(){
  10. NSLog(@"task1 -- %@",[NSThread currentThread]);
  11. };
  12. void(^task2) (void) = ^(){
  13. NSLog(@"task2 -- %@",[NSThread currentThread]);
  14. };
  15. void(^task3) (void) = ^(){
  16. NSLog(@"task3 -- %@",[NSThread currentThread]);
  17. };
  18. //添加任务到队列
  19. dispatch_async(mainAsync, task1);
  20. dispatch_async(mainAsync, task2);
  21. dispatch_async(mainAsync, task3);
  22. NSLog(@"end");
  23. }

  来看看打印情况:

  1. 2019-04-01 16:23:52.002922+0800 wftest[2320:37104] 主线程 -- <NSThread: 0x600001676a80>{number = 1, name = main}
  2. 2019-04-01 16:23:52.003178+0800 wftest[2320:37104] begin
  3. 2019-04-01 16:23:52.003342+0800 wftest[2320:37104] end
  4. 2019-04-01 16:23:52.092118+0800 wftest[2320:37104] task1 -- <NSThread: 0x600001676a80>{number = 1, name = main}
  5. 2019-04-01 16:23:52.092324+0800 wftest[2320:37104] task2 -- <NSThread: 0x600001676a80>{number = 1, name = main}
  6. 2019-04-01 16:23:52.092497+0800 wftest[2320:37104] task3 -- <NSThread: 0x600001676a80>{number = 1, name = main}

  大家可以注意到这里的几个情况:

1.虽然是异步的,但是三个任务仍然按顺序调度执行。

2.先执行了begin,紧接着执行的了end.然后才执行了三个任务,也就是说,主线程有空闲的时候才执行这三个任务。

我们说下deadlock死锁:是两个或者更多的线程之间出现的情况:比如第一个线程在等待第二个线程的完成才能继续执行,而第二个线程在等待第一个线程的完成才能继续执行。

看起来似乎异步更有用,效率更高,那么同步有什么用呢,我们说下同步的作用。

1.首先,同步肯定是保证了任务执行的顺序。

2.可以让后面的异步任务要依赖于某一个同步的任务。比如,必须让用户登录之后,才允许他下载电影。

我们看下代码:

  1. //同步+异步
  2. -(void)loadMovies{
  3. dispatch_async(dispatch_get_global_queue(0, 0), ^{//开辟一条子线程
  4. NSLog(@"开辟了子线程----%@",[NSThread currentThread]);
  5. dispatch_sync(dispatch_get_global_queue(0, 0), ^{
  6. //登录,在当前的线程执行
  7. NSLog(@"登录了---%@", [NSThread currentThread]);
  8. sleep(3);
  9. });
  10.  
  11. //2.同时下载3部电影
  12. dispatch_async(dispatch_get_global_queue(0, 0), ^{
  13. NSLog(@"正在下载第一部电影---%@",[NSThread currentThread]);
  14. });
  15. dispatch_async(dispatch_get_global_queue(0, 0), ^{
  16. NSLog(@"正在下载第二部电影---%@",[NSThread currentThread]);
  17. });
  18. dispatch_async(dispatch_get_global_queue(0, 0), ^{
  19. NSLog(@"正在下载第三部电影---%@",[NSThread currentThread]);
  20. });
  21.  
  22. dispatch_sync(dispatch_get_main_queue(), ^{
  23. [NSThread sleepForTimeInterval:1.0];
  24. NSLog(@"计算机将在三秒后关闭 --%@",[NSThread currentThread]);
  25. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  26. NSLog(@"关机了---%@", [NSThread currentThread]);
  27. });
  28. });
  29. });
  30. }

  然后我们来看打印log:

  1. 2019-04-01 17:52:46.491858+0800 wftest[5359:81060] 开辟了子线程----<NSThread: 0x600002ff8580>{number = 3, name = (null)}
  2. 2019-04-01 17:52:46.492263+0800 wftest[5359:81060] 登录了---<NSThread: 0x600002ff8580>{number = 3, name = (null)}
  3. 2019-04-01 17:52:49.497610+0800 wftest[5359:81063] 正在下载第一部电影---<NSThread: 0x600002ffaa00>{number = 4, name = (null)}
  4. 2019-04-01 17:52:49.497634+0800 wftest[5359:81062] 正在下载第三部电影---<NSThread: 0x600002ffefc0>{number = 6, name = (null)}
  5. 2019-04-01 17:52:49.497654+0800 wftest[5359:81061] 正在下载第二部电影---<NSThread: 0x600002ffef40>{number = 5, name = (null)}
  6. 2019-04-01 17:52:50.498825+0800 wftest[5359:80998] 计算机将在三秒后关闭 --<NSThread: 0x600002f9d600>{number = 1, name = main}
  7. 2019-04-01 17:52:53.752223+0800 wftest[5359:80998] 关机了---<NSThread: 0x600002f9d600>{number = 1, name = main}

  我们注意到这几个方面:虽然下载电影的时候,又开启了三个新的线程,但是他们仍然要等待登录后,才能执行,以及最后,计算机回到主队列去关闭计算机的时候,也是等电影下载完毕。这是因为主队列这里的也是同步任务。前面也是同步任务。

接下来我们来看下dispatch_time的延迟操作

什么时候使用dispatch_after呢?

1.最好坚持在主队列上使用dispatch_after。而不是在自定义串行队列上,并发队列也尽量不要使用。

2.主队列(串行)是使用dispatch_after的最好选择。xcode也提供了自动完成模板。

我们来看下代码:

  1. //延迟执行
  2. -(void)delay{
  3. dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC));
  4. void(^task)(void)=^(){
  5. NSLog(@"%@",[NSThread currentThread]);
  6. };
  7. //主队列
  8. dispatch_after(when, dispatch_get_main_queue(), task);
  9. NSLog(@"come here");
  10. }

  来看打印:

  1. 2019-04-02 10:55:47.020688+0800 wftest[2069:28686] come here
  2. 2019-04-02 10:55:49.216037+0800 wftest[2069:28686] <NSThread: 0x6000018e1c40>{number = 1, name = main}

IOS提供的一些方便使用的延迟:

  1. //延迟执行
  2. -(void)after{
  3. [self.view performSelector:@selector(setBackgroundColor:) withObject:[UIColor orangeColor] afterDelay:1.0];
  4. }

  再来看看线程安全:

线程安全是多线程不可避免的问题。

dispatch_once以线程安全的方式执行,仅且执行代码一次。她会给代码设立一个临界区。试图访问临界区(即要传递给dispatch_onece的代码)的不同线程,在临界区已经有一个线程在执行的情况下会被阻塞,直到临界区完成为止。

我们来看下使用dispatch_once来实现单例线程安全:

  1. //使用dispatch_once实现线程安全的单例
  2. +(instancetype)sharedSingleton{
  3. static id instance;
  4. static dispatch_once_t onceToken;
  5. dispatch_once(&onceToken, ^{
  6. instance = [[self alloc] init];
  7. });
  8. return instance;
  9. }

 如果一个单例中的单例属性是一个可变对象。那么就要考虑线程安全问题了。比如NSMutableArray。可能会出现一个线程正在读取,另外一个线程正在修改。这样就会出现线程不安全的情况。在GCD中可以通过dispatch_barrier_async来进行创建读写锁,这样一个解决方案。

接下来,我们来看下调度组(dispatch_group):

调度组的实现原理:类似引用计数,进行+1,-1;

应用场景:

比如当你开启了下载任务,当下载三个任务,只有等这三个任务全部下载完毕后,才能下一步做事情。这个时候就可以用到调度组,这个调度组,就能监听它里面的任务是否执行完毕:

  1. //调度组
  2. -(void)groupDispatch{
  3. //1.创建调度组
  4. dispatch_group_t group = dispatch_group_create();
  5. //2.获取全局队列
  6. dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
  7. //3.创建三个下载任务
  8. void(^task1)(void) = ^(){
  9. NSLog(@"%@----下载片头",[NSThread currentThread]);
  10. };
  11.  
  12. dispatch_group_enter(group);//引用计算+1
  13. void (^task2) (void) = ^(){
  14. NSLog(@"%@---下载内容",[NSThread currentThread]);
  15. [NSThread sleepForTimeInterval:3.0];
  16. NSLog(@"----下载内容完毕");
  17. dispatch_group_leave(group);//引用计数-1
  18. };
  19.  
  20. dispatch_group_enter(group);//引用计数+1
  21. void(^task3)(void)=^(){
  22. NSLog(@"%@----下载片尾",[NSThread currentThread]);
  23. dispatch_group_leave(group);//引用计数-1
  24. };
  25.  
  26. //4.需要将我们的队列和任务放到组内去监控
  27. dispatch_group_async(group, queue, task1);
  28. dispatch_group_async(group, queue, task2);
  29. dispatch_group_async(group, queue, task3);
  30.  
  31. //5.监听函数
  32. // 参数2,表示参数3这里的代码在哪个队列中执行
  33. dispatch_group_notify(group, dispatch_get_main_queue(), ^{
  34. //表示组内所有的任务都完成之后,执行这里的代码
  35. NSLog(@"把下载好的视频按顺序拼接好,然后显示在UI上播放%@",[NSThread currentThread]);
  36. });
  37.  
  38. }

  看打印:

  1. 2019-04-02 16:18:45.168288+0800 wftest[12368:305146] <NSThread: 0x6000023d5fc0>{number = 4, name = (null)}---下载内容
  2. 2019-04-02 16:18:45.168288+0800 wftest[12368:305147] <NSThread: 0x6000023d5f00>{number = 3, name = (null)}----下载片头
  3. 2019-04-02 16:18:45.168288+0800 wftest[12368:305149] <NSThread: 0x6000023e9c80>{number = 5, name = (null)}----下载片尾
  4. 2019-04-02 16:18:48.174292+0800 wftest[12368:305146] ----下载内容完毕
  5. 2019-04-02 16:18:48.174604+0800 wftest[12368:305085] 把下载好的视频按顺序拼接好,然后显示在UI上播放<NSThread: 0x6000023b25c0>{number = 1, name = main}

  dispatch_group_enter手动通知group任务已经开始。注意,enter和leave必须成对。否则会造成诡异崩溃问题。

最后,再来看定时源事件和子线程的运行循环:

  1. -(void)myMain{
  2. //1.定义一个定时器
  3. NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeEvnet) userInfo:nil repeats:YES];
  4. //2.将定时器加入到运行循环中,只有当加入到运行循环中,他才知道这个时候,有一个定时任务
  5. [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
  6. }
  7.  
  8. -(void)timeEvnet{
  9. NSLog(@"%d----%@",self.count,[NSThread currentThread]);
  10. if(self.count++ == 10){
  11. NSLog(@"挂了");
  12. //停止当前的运行循环
  13. CFRunLoopStop(CFRunLoopGetCurrent());
  14. }
  15. }

  




ios高级开发之多线程(三)GCD技术的更多相关文章

  1. ios高级开发之多线程(二)NSThread技术

    多线程技术是有多套解决方案的,那么我们该如何选择呢? 技术方案 简介 语言 线程生命周期 使用频率 pthread 1.一套通用的多线程API 2.适用于UNIX,linux,windows等 3.跨 ...

  2. IOS高级开发之多线程(四)NSOperation

    1.什么是NSOperation,NSOperationQueue? NSOperation是一个抽象的基类,表示一个独立的计算单元,可以为子类提供有用且线程安全的建立状态,优先级,依赖和取消等操作. ...

  3. ios高级开发之多线程(一)

    1.概念: 多线程(multithreading)到底是什么呢,它是指在软件或者硬件上实现多个线程并发执行的技术.具有多线程能力的计算机因有硬件的支持,而能够在同一时间执行多个线程,进而提升整体处理性 ...

  4. iOS 高级开发 runtime(三)

    三 .动态添加方法 我们可以通过runtime动态地添加方法.那么到底啥叫动态添加方法呢?动态添加方法就是当我们程序运行时才知道我们应该调用哪个方法.我们首先需要了解这一点,当我们编写完一段代码后,我 ...

  5. IOS高级开发之多线程(五)NSOperation 2

    接着看NSOperation.NSOperationQueue线程间的通信: 应用场景:比如我们经常把一些耗时的操作比如下载图片放在子线程,那么当这个完成之后,我们就需要回到主线程,这个时候就需要用到 ...

  6. 移动开发在路上-- IOS移动开发系列 多线程三

    这一次说一点概念性的东西,也是为后边做一些基础 HTTP协议的基本概念 http协议的基本概念 全称“超文本传输协议”,浏览器和服务器之间的通信规则 HTTp协议永远都是客户端发起的请求,服务器回送响 ...

  7. (转发)IOS高级开发~Runtime(三)

    11.系统类的方法实现部分替换 - (void) methodExchange { Method m1 = class_getInstanceMethod([NSStringclass],@selec ...

  8. (转发)IOS高级开发~Runtime(四)

    用C代替OC: #import <objc/runtime.h> #import <objc/message.h> #import <stdio.h> extern ...

  9. (转发)IOS高级开发~Runtime(二)

    一些公用类: @interface ClassCustomClass :NSObject{ NSString *varTest1; NSString *varTest2; NSString *varT ...

随机推荐

  1. 2017(2)数据库设计,数据库设计过程,ER模型,规范化理论

    试题二(共 25 分〉 阅读以下关于系统数据分析与建模的叙述,在答题纸上回答问题1 至问题 3. [说明] 某软件公司受快递公司委托,拟开发一套快递业务综合管理系统,实现快递单和物流信息的综合管理.项 ...

  2. Docker 私有仓库建立(加密和用户验证)

    (一)生成证书1.mkdir /certs2.cd /certs 3.生成自签名证书 sudo openssl req -newkey rsa:2048 -new -nodes -x509 -days ...

  3. iframe父页面与子页面赋值

    最近因为公司之前的系统用iframe,里面的高度不能自适应,导致了很多问题,今天特意拿来研究一下,从网上找了一些方法试验了一下,这里记录一下成功的方法 1.父页面获取子页面的高度,并给父页面赋值 父页 ...

  4. [linux系统]XFS (vda3):Corruption detected.Unmount and run xfs_repair 简单解决办法

    今天kvm的centos系统,rm -rf时报错,具体如下: rm: cannot remove ‘log-0/case1/log_net’: Input/output errorrm: cannot ...

  5. pandas处理时间序列(4): 移动窗口函数

    六.移动窗口函数 移动窗口和指数加权函数类别如↓: rolling_mean 移动窗口的均值 pandas.rolling_mean(arg, window, min_periods=None, fr ...

  6. 关于PL/SQL Developer与Oracle客户端

    这是一个很简单的知识点,但这些年遇到过太多初学者反而受其困扰,所以还是决定记录一下. 背景:国内使用Oracle的群体,几乎都会使用PL/SQL Developer这个图形化的工具进行日常数据维护.这 ...

  7. Centos7 Lnmp的环境搭建

    centos  版本 [root@localhost ~]# cat /etc/redhat-release CentOS Linux release 7.2.1511 (Core) 关闭防火墙 sy ...

  8. orcl数据库锁等级研究小记

    上周通过orcl 悲观锁的方式解决了一个并发临界值的问题.现在来研究下orcl各中锁的机制以及如何手动释放锁. 首先,通过查阅资料,先了解下数据的的各种操作语言分类. SQL语言共分为四大类:数据查询 ...

  9. selenium webdriver报错 ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。

    昨天跑的好好的代码,今天突然报错: ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接. 调查一下,原来是Chrome自动升级,而chrom ...

  10. 2018-2019-2 《网络对抗技术》Exp3 免杀原理与实践 20165215

    目录 基础问题回答 (1)杀软是如何检测出恶意代码的? (2)免杀是做什么? (3)免杀的基本方法有哪些? 实践内容 任务一:正确使用msf编码器,msfvenom生成如jar之类的其他文件,veil ...