任务和队列
-
任务:就是执行操作的意思,换句话说就是你在线程中执行的那段代码。在GCD中是放在block中的。执行任务有两种方式:同步执行和异步执行。两者的主要区别是:是否具备开启新线程的能力。
- 同步执行(sync):只能在当前线程中执行任务,不具备开启新线程的能力
- 必须等待当前语句执行完毕,才会执行下一条语句
- 不会开启线程
- 在当前主线程执行 block 的任务
dispatch_sync(queue, block);
- ** 异步执行(async)**:可以在新的线程中执行任务,具备开启新线程的能力
- 不用等待当前语句执行完毕,就可以执行下一条语句
- 会开启线程执行 block 的任务
- 异步是多线程的代名词
dispatch_async(queue, block);
-
队列:这里的队列指任务队列,即用来存放任务的队列。队列是一种特殊的线性表,采用FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。在GCD中有四种队列:串行队列、并发队列、主队列、全局队列。
-
串行队列(Serial Dispatch Queue):让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
- 一次只能"调度"一个任务
dispatch_queue_create("queue", NULL);
或者dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
-
并发队列(Concurrent Dispatch Queue):可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务),
- 一次可以"调度"多个任务
- 并发功能只有在异步(dispatch_async)函数下才有效
dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
-
主队列
- 专门用来在主线程上调度任务的队列
- 不会开启线程
- 在主线程空闲时才会调度队列中的任务在主线程执行
- dispatch_get_main_queue();
-
全局队列
- 执行过程和并发队列一致,参考并发队列
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
-
小结:在以后的使用中,记住下面的就可以了!
串行队列同步执行不会开辟线程,所有block任务之间是同步执行的。
串行队列异步执行仅会开辟一个(除主线程外)新的线程,所有block任务之间是同步(按顺序)执行的。
并发队列同步执行和串行队列同步执行一样,都不会开辟新线程,block任务之间是同步执行的。
并发队列异步执行结果中看到开辟了多个线程,并且执行顺序也不是顺序执行。
- 开不开线程由执行任务的函数决定
- 异步开,异步是多线程的代名词
- 同步不开
- 开几条线程由队列决定
- 串行队列开一条线程(GCD会开一条,NSOperation Queue最大并发数为1时也可能开多条)
- 并发队列开多条线程,具体能开的线程数量由底层线程池决定
- (void)testGCD{ //任务:同步、异步 队列:串行、并发 /***************** 队列的创建方法 ********************/ //dispatch_queue_create创建队列 //第一个参数:唯一标识符(自定义) //第二个参数:区分串行、并行 //串行(DISPATCH_QUEUE_SERIAL)并行(DISPATCH_QUEUE_CONCURRENT) dispatch_queue_t queue01 = dispatch_queue_create("chuan.net", DISPATCH_QUEUE_SERIAL); dispatch_queue_t queue02 = dispatch_queue_create("bing.net", DISPATCH_QUEUE_CONCURRENT); //主队列也属于特殊的串行队列 dispatch_queue_t queue03 = dispatch_get_main_queue(); //全局并发队列 第一个参数:优先级 第二个参数:暂时没用 dispatch_queue_t queue04 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); /**************** 任务的创建方法 *****************/ dispatch_sync(queue01, ^{ //同步执行任务 }); dispatch_async(queue01, ^{ //异步执行任务 }); /*************************************/ /** 任务和队列的组合方式有以下几种: 1、同步执行+并发队列 2、同步执行+串行队列 3、异步执行+并发队列 4、异步执行+串行队列 5、同步执行+主队列 6、异步执行+主队列 同步执行不具备开启新线程的能力,同步任务需要等待队列的任务执行结束 */ /****************** 同步执行+并发队列 *******************/ //特点:在当前线程中执行任务,不会开启新线程,执行完一个任务,再执行下一个任务 dispatch_queue_t queue11 = dispatch_queue_create("tong_bing.net", DISPATCH_QUEUE_CONCURRENT); dispatch_sync(queue11, ^{ NSLog(@"同步执行+并发队列--追加任务1"); }); dispatch_sync(queue11, ^{ NSLog(@"同步执行+并发队列--追加任务2"); }); dispatch_sync(queue11, ^{ NSLog(@"同步执行+并发队列--追加任务3"); }); NSLog(@"同步执行+并发队列--结束"); /****************** 同步执行+串行队列 *******************/ //特点:不会开启新线程,在当前线程执行任务。任务是串行,执行完一个,再执行下一个。 dispatch_queue_t queue12 = dispatch_queue_create("tong_chuan.net", DISPATCH_QUEUE_SERIAL); dispatch_sync(queue12, ^{ NSLog(@"同步执行+串行队列--追加任务1"); }); dispatch_sync(queue12, ^{ NSLog(@"同步执行+串行队列--追加任务2"); }); dispatch_sync(queue12, ^{ NSLog(@"同步执行+串行队列--追加任务3"); }); NSLog(@"同步执行+串行队列--结束"); /****************** 异步执行+并发队列 *******************/ //特点:可以开启多个线程,任务交替(同时)执行。 dispatch_queue_t queue13 = dispatch_queue_create("yi_bing.net", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue13, ^{ NSLog(@"异步执行+并发队列--追加任务1"); }); dispatch_async(queue13, ^{ NSLog(@"异步执行+并发队列--追加任务2"); }); dispatch_async(queue13, ^{ NSLog(@"异步执行+并发队列--追加任务3"); }); NSLog(@"异步执行+并发队列--结束"); /****************** 异步执行+串行队列 *******************/ //特点:会开启新线程,但是因为任务是串行的,执行完一个任务,再执行下一个任务。 dispatch_queue_t queue14 = dispatch_queue_create("yi_chuan.net", DISPATCH_QUEUE_SERIAL); dispatch_async(queue14, ^{ NSLog(@"异步执行+串行队列--追加任务1"); }); dispatch_async(queue14, ^{ NSLog(@"异步执行+串行队列--追加任务2"); }); dispatch_async(queue14, ^{ NSLog(@"异步执行+串行队列--追加任务3"); }); NSLog(@"异步执行+串行队列--结束"); /****************** 同步执行+主队列 *******************/ //在不同线程中调用结果也是不一样,在主线程中调用会出现死锁,而在其他线程中则不会。 /********** 主线程中调用 ********/ //互相等待卡住不可行 //其他线程 [NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil]; /****************** 异步执行+主队列 *******************/ //特点:只在主线程中执行任务,执行完一个任务,再执行下一个任务 dispatch_queue_t queue16 = dispatch_get_main_queue(); dispatch_async(queue16, ^{ NSLog(@"异步执行+主队列--追加任务1"); }); dispatch_async(queue16, ^{ NSLog(@"异步执行+主队列--追加任务2"); }); dispatch_async(queue16, ^{ NSLog(@"异步执行+主队列--追加任务3"); }); NSLog(@"异步执行+主队列--结束"); /*************** dispatch_after延时操作 ******************/ //dispatch_after函数并不是在指定时间之后才开始执行处理,而是在指定时间之后将任务追加到主队列中。 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ //2.0秒后一步追加任务代码到主队列,并开始执行 NSLog(@"延时执行操作"); }); /******************** dispatch_once ************************/ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSLog(@"一次性代码"); }); /***************** dispatch_apply快速迭代 *********************/ //dispatch_apply按照指定次数将指定任务追加到指定的队列中,并等待全部队列执行结束。 //如果是在串行队列中使用 dispatch_apply,那么就和 for 循环一样,按顺序同步执行。可这样就体现不出快速迭代的意义了。 //无论是在串行队列,还是异步队列中,dispatch_apply 都会等待全部任务执行完毕,这点就像是同步操作,也像是队列组中的 dispatch_group_wait方法 dispatch_queue_t queue21 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_apply(10, queue21, ^(size_t index) { NSLog(@"快速迭代==%zd",index); }); /****************** dispatch_group队列组 ********************/ //队列组中的任务执行完毕返回主线程:把任务放入队列中再放入队列组 dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"队列组--追加任务1"); }); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"队列组--追加任务2"); }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"队列组--回到主线程"); }); /****************** dispatch_group_wait ********************/ //暂停当前线程(阻塞当前线程),等待指定的group中的任务执行完成后,才会往下继续执行。 dispatch_group_t group23 = dispatch_group_create(); dispatch_group_async(group23, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"队列组--追加任务3"); }); dispatch_group_async(group23, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"队列组--追加任务4"); }); //等待上面的任务全部完成后,会往下继续执行(会阻塞当前线程) dispatch_group_wait(group23, DISPATCH_TIME_FOREVER); /********** dispatch_group_enter、dispatch_group_leave **********/ //ispatch_group_enter标志着一个任务追加到group,执行一次,相当于group中未执行完毕任务数+1 //dispatch_group_leave标志着一个任务离开了group,执行一次,相当于group中未执行完毕任务数-1。 //当group中未执行完毕任务数为0的时候,才会使dispatch_group_wait解除阻塞,以及执行追加到dispatch_group_notify中的任务。 dispatch_group_t group24 = dispatch_group_create(); dispatch_queue_t queue001 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_enter(group24); dispatch_async(queue001, ^{ NSLog(@"dispatch_group_enter追加任务1"); dispatch_group_leave(group24); }); dispatch_group_enter(group24); dispatch_async(queue001, ^{ NSLog(@"dispatch_group_enter追加任务2"); dispatch_group_leave(group24); }); dispatch_group_notify(group24, dispatch_get_main_queue(), ^{ NSLog(@"dispatch_group_enter主线程"); }); } - (void)syncMain{ dispatch_queue_t queue15 = dispatch_get_main_queue(); dispatch_sync(queue15, ^{ NSLog(@"同步执行+主队列--追加任务1"); }); dispatch_sync(queue15, ^{ NSLog(@"同步执行+主队列--追加任务2"); }); dispatch_sync(queue15, ^{ NSLog(@"同步执行+主队列--追加任务3"); }); NSLog(@"同步执行+主队列--结束"); } //栅栏方法 - (void)barrier{ dispatch_queue_t queue = dispatch_queue_create("barrier.net", DISPATCH_QUEUE_CONCURRENT); dispatch_sync(queue, ^{ NSLog(@"追加任务1"); }); dispatch_sync(queue, ^{ NSLog(@"追加任务2"); }); dispatch_barrier_async(queue, ^{ NSLog(@"栅栏函数--追加任务3"); }); dispatch_sync(queue, ^{ NSLog(@"追加任务4"); }); } - (void)semaphore{ /*************** dispatch_semaphore信号量 ****************/ //Dispatch Semaphore 提供了三个函数。 //dispatch_semaphore_create:创建一个Semaphore并初始化信号的总量 //dispatch_semaphore_signal:发送一个信号,让信号总量加1 //dispatch_semaphore_wait:可以使总信号量减1,当信号总量为0时就会一直等待(阻塞所在线程),否则就可以正常执行。 dispatch_queue_t queue01 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); dispatch_async(queue01, ^{ dispatch_semaphore_signal(semaphore); //加1 //操作 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//减1 }); }
小结:
- 并发队列同步执行和串行队列同步执行一样,都不会开辟新线程,block任务之间是同步执行的!
- 并发队列异步执行结果中看到开辟了多个线程,并且执行顺序也不是顺序执行。因为异步开多线程的代名词,并发是开多条线程的代名词
- 有多个线程,操作进来之后它会将这些队列安排在可用的处理器上,同时保证先进来的任务优先处理。
- 以先进先出的方式,并发调度队列中的任务执行
- 如果当前调度的任务是同步执行的,会等待任务执行完成后,再调度后续的任务
- 如果当前调度的任务是异步执行的,同时底层线程池有可用的线程资源,会再新的线程调度后续任务的执行