我在前面的博客中《GCD实践——串行队列/并发队列与IOS多线程详解》中对iOS中的同步异步、串行并行做了较为详细的讲解。在之后的几篇GCD实践的博客中对GCD的使用也有较为详细的实现。但是我们要注意的是,那里用到的GCD是别人对苹果的原生GCD接口封装后的使用,虽然用起来更为方便,但是为了有全面的学习,我还是推荐去用苹果原生的GCD。该案例代码上传至 https://github.com/chenyufeng1991/MoreGCD 。代码中已经有部分注释对理解代码很有帮助。下面我来一一进行讲解。
只要是同步执行的任务,都会在当前线程执行,不会另开线程。 只要是异步任务执行的任务,都会另开线程,在别的线程执行。 同步操作会阻塞当前线程,直到block中的任务执行完毕,然后当前线程才会继续往下执行。(block在主线程中执行) 异步操作,当前线程会直接往下执行,不会阻塞当前线程。 (因为block在新线程中执行)
串行队列:一次只执行一个线程。 并行队列:一次可以执行多个线程。
(1)自己创建一个并发队列,并同步执行
//dispatch_queue_t 也可以自己定义,如果要自定义,可以用dispatch_queue_create方法。 //当然也可以使用系统提供的global_queue,main_queue建议该队列使用完后,进行release。
dispatch_queue_t syncQueue = dispatch_queue_create("my.concurrent.syncQueue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
dispatch_sync(syncQueue, ^{
NSLog(@"2");
[NSThread sleepForTimeInterval:3];
NSLog(@"3");
});
NSLog(@"4");
dispatch_release(syncQueue);//最好需要释放
(2)自己创建一个并发队列,异步执行
dispatch_queue_t asyncQueue = dispatch_queue_create("my.concurrent.asyncQueue", DISPATCH_QUEUE_CONCURRENT); NSLog(@"1"); dispatch_async(asyncQueue, ^{ NSLog(@"2"); [NSThread sleepForTimeInterval:3]; NSLog(@"3"); }); NSLog(@"4"); dispatch_release(asyncQueue);
(3)自己创建一个串行队列,同步执行
dispatch_queue_t syncQueue = dispatch_queue_create("my.serial.syncQueue", DISPATCH_QUEUE_SERIAL); NSLog(@"1"); dispatch_sync(syncQueue, ^{ NSLog(@"2"); [NSThread sleepForTimeInterval:3]; NSLog(@"3"); }); NSLog(@"4"); dispatch_release(syncQueue);
(4)自己创建一个串行队列,异步执行
dispatch_queue_t asyncQueue = dispatch_queue_create("my.serial.asyncQueue", DISPATCH_QUEUE_SERIAL); NSLog(@"1"); //第一个参数是指定了一个GCD队列,第二个参数是分配事务到该队列。 dispatch_async(asyncQueue, ^{ NSLog(@"2"); [NSThread sleepForTimeInterval:3]; NSLog(@"3"); }); NSLog(@"4"); dispatch_release(asyncQueue);
(5)关于系统提供的线程
dispatch_global_queue :并行队列
dispatch_private_queue:串行队列
dispatch_main_queue:主线程
以下代码是异步执行耗时操作,并更新UI的代码:
dispatch_async(dispatch_get_global_queue(0, 0), ^{ //进入另一个线程,处理耗时的代码块 dispatch_async(dispatch_get_main_queue(), ^{ //返回主线程刷新 }); });
(6)一次性执行某个操作,并在应用生命周期内仅执行一次
//在该应用声明周期内,下面的代码只会被执行一次。并且是线程安全的。 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSLog(@"仅会被执行一次"); });
(7)线程组,是一种同步机制,可以让某些线程先执行,某些线程最后执行,以控制线程的执行顺序。
//GCD的高级用法 线程组;线程组和信号量机制都可以实现队列的同步。 __block int i; __block int j; dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{ i = 1; }); dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{ j = 2; }); dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{ NSLog(@"%d",i+j); });
(8)线程的延迟执行
NSLog(@"1"); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"延迟3s执行这里"); });
//信号量 /** * 创建一个信号量。参数指定信号量的起始值。这个数字是你可以访问的信号量,不需要先去增加它的数量(增加信号量也叫作发射信号量)。 初始value = 0时,信号量--,小于0,wait线程阻塞。然后执行signal,信号量++,激活wait线程。 * */ dispatch_semaphore_t sema = dispatch_semaphore_create(0); dispatch_async(dispatch_get_global_queue(0, 0), ^{ dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); NSLog(@"chen"); }); dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"yu"); dispatch_semaphore_signal(sema); });
(10)同一个线程中的不同任务实现同步。
使用dispatch_barrier_queue实现。注意dispatch_barrier_queue的同步控制和线程组、信号量的同步机制是不一样的,dispatch_barrier_queue是对于同一个队列中的不同任务而言的,线程组和信号量是对于不同线程而言的。
//dispatch_barrier_async,对于同一个队列中的不同任务而言,在dispatch_barrier_async之前的先执行,在dispatch_barrier_async后面的后执行 //如下面的代码所示:1和2的执行顺序不一定,但一定在dispatch_barrier_async之前执行,3和4的执行顺序不一定,但一定在dispatch_barrier_async之后执行。 //注意dispatch_barrier_async的同步控制和线程组、信号量的同步机制粒度大小是不一样的,dispatch_barrier_async是对于同一个队列中的不同任务而言的,线程组和信号量是对于不同线程而言的。 dispatch_queue_t queue = dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ NSLog(@"1"); }); dispatch_async(queue, ^{ NSLog(@"2"); }); dispatch_barrier_async(queue, ^{ NSLog(@"dispatch_barrier_async"); }); dispatch_async(queue, ^{ NSLog(@"3"); }); dispatch_async(queue, ^{ NSLog(@"4"); });
(11)dispatch_apply是把某个代码块执行N遍。
//dispatch_apply是把某个代码块执行N遍 dispatch_apply(5, dispatch_get_global_queue(0, 0), ^(size_t index) { NSLog(@"%zu",index); });
GCD在项目中会经常会用到,应该需要熟练掌握。