iOS开发之GCD使用
一、队列
1、串行队列(Serial Dispatch Queue)
// 串行队列的创建方法
dispatch_queue_t queue = dispatch_queue_create("标识符", DISPATCH_QUEUE_SERIAL);
2、并发队列(Concurrent Dispatch Queue)
// 并发队列的创建方法
dispatch_queue_t queue = dispatch_queue_create("标识符", DISPATCH_QUEUE_CONCURRENT);
3、系统主队列(Main Dispatch Queue)
// 主队列的获取方法 dispatch_queue_t queue = dispatch_get_main_queue();//相当于同步串行队列
dispatch_queue_t queue = dispatch_queue_create("标识符", DISPATCH_QUEUE_SERIAL); dispatch_sync(queue, ^{ });
4、全局并发队列(Global Dispatch Queue)
// 全局并发队列的获取方法 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)//相当于异步并行队列
dispatch_queue_t queue = dispatch_queue_create("标识符",DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ });
二、任务
GCD 提供了同步执行任务的创建方法 dispatch_sync 和异步执行任务创建方法 dispatch_async。
// 同步执行任务创建方法
dispatch_sync(queue, ^{
// 这里放同步执行任务代码
});
// 异步执行任务创建方法
dispatch_async(queue, ^{
// 这里放异步执行任务代码
});
队列和任务可以随意组合,达到不同目的。注意: 『主线程』 中调用 『主队列』+『同步执行』 会导致死锁问题。这是因为 主队列中追加的同步任务 和 主线程本身的任务 两者之间相互等待,阻塞了 『主队列』,最终造成了主队列所在的线程(主线程)死锁问题。
三、GCD 线程间的通信
//线程间通信 (开启子线程处理完耗时操作,回到主线程刷新UI)
- (void)communication {
// 获取全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 获取主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(queue, ^{
// 异步追加任务 1
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
// 回到主线程
dispatch_async(mainQueue, ^{
// 追加在主线程中执行的任务
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
});
});
}
四、GCD 栅栏方法 (dispatch_barrier_async/dispatch_barrier_sync)
dispatch_barrier_async就是将异步执行分成两组操作,而且第一组操作执行完之后,才能开始执行第二组操作。
- (void)gcdBarrier
{
// 创建队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----2-----%@", [NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"----barrier-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----3-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----4-----%@", [NSThread currentThread]);
});
// 可以看出在执行完栅栏前面的操作之后,才执行栅栏操作,最后再执行栅栏后边的操作
}//珊栏方法的
注意:1. dispatch_barrier_async , 会将添加到queue前面的任务执行完之后,才会执行后面的任务,并不会阻塞当前的线程;
2.dispatch_barrier_sync , 会将添加到queue前面的任务执行完之后,才会执行后面的任务,并且会阻塞当前的线程
五、GCD 延时执行方法:dispatch_after
// 延时执行方法 dispatch_after
- (void)after {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"asyncMain---begin");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2.0 秒后异步追加任务代码到主队列,并开始执行
NSLog(@"after---%@",[NSThread currentThread]); // 打印当前线程
});
}
六、GCD 一次性代码(只执行一次):dispatch_once
dispatch_once能保证任务只会被执行一次,即使同时多线程调用也是线程安全的。常用于创建单例、swizzeld method等功能。
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//创建单例、method swizzled或其他任务
});
七、GCD 快速迭代方法:dispatch_apply
void dispatch_apply(size_t iterations, dispatch_queue_t queue,
DISPATCH_NOESCAPE void (^block)(size_t));
该函数按指定的次数将指定的Block追加到指定的Dispatch Queue中,并等待全部处理执行结束,好处是可以重复执行某项操作并复用我们的Block了!
第一个参数为重复次数;
第二个参数为追加对象的Dispatch Queue;
第三个参数为追加的操作,追加的Block中带有参数,这是为了按第一个参数重复追加Block并区分各个Block而使用。
dispatch_apply函数是dispatch_sync函数和Dispatch Group的关联API。这是因为该函数会等待这些操作执行完毕再返回,内部操作执行是否同步依赖于传入的queue,外部就必定是同步的。
- (void)gcdDispatchApply
{
//获取一个全局队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//dispatch_queue_t queue = dispatch_queue_create("com.gcd.dispatchApply.serialQueue", NULL);
//在全局队列queue上执行十次输出操作
dispatch_apply(10, queue, ^(size_t index) {
NSLog(@"%zu", index);
});
NSLog(@"done!");
}
注意:当传入的queue是并行队列时,重复执行的操作输出是无序的,done输出始终在最后。
当传入的queue是串行队列时,重复执行的操作输出是有序的,done输出始终在最后。
八、GCD 队列组:dispatch_group
有时候我们会有这样的需求:分别异步执行2个耗时任务,然后当2个耗时任务都执行完毕后再回到主线程执行任务。这时候我们可以用到 GCD 的队列组。
- 调用队列组的
dispatch_group_async先把任务放到队列中,然后将队列放入队列组中。或者使用队列组的dispatch_group_enter、dispatch_group_leave组合来实现dispatch_group_async。 - 调用队列组的
dispatch_group_notify回到指定线程执行任务。或者使用dispatch_group_wait回到当前线程继续向下执行(会阻塞当前线程)。
1、dispatch_group_notify
监听 group 中任务的完成状态,当所有的任务都执行完成后,追加任务到 group 中,并执行任务。
// 队列组 dispatch_group_notify
- (void)groupNotify {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任务 1
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任务 2
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的异步任务 1、任务 2 都执行完毕后,回到主线程执行下边任务
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"group---end");
});
}
//当前面两个任务都执行完成之后,才执行 dispatch_group_notify 相关 block 中的任务。
2、dispatch_group_wait
暂停当前线程(阻塞当前线程),等待指定的 group 中的任务执行完成后,才会往下继续执行。
// 队列组 dispatch_group_wait
- (void)groupWait {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任务 1
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任务 2
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
});
// 等待上面的任务全部完成后,会往下继续执行(会阻塞当前线程)
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"group---end");
}
//当前面两个任务执行完成之后,才执行 dispatch_group_wait 之后的操作。但是,使用dispatch_group_wait 会阻塞当前线程。
3、dispatch_group_enter、dispatch_group_leave
dispatch_group_enter 标志着一个任务追加到 group,执行一次,相当于 group 中未执行完毕任务数 +1
dispatch_group_leave 标志着一个任务离开了 group,执行一次,相当于 group 中未执行完毕任务数 -1。
当 group 中未执行完毕任务数为0的时候,才会使 dispatch_group_wait 解除阻塞,以及执行追加到 dispatch_group_notify 中的任务。
/**
* 队列组 dispatch_group_enter、dispatch_group_leave
*/
- (void)groupEnterAndLeave {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_enter(group);
dispatch_async(queue, ^{
// 追加任务 1
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
// 追加任务 2
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的异步操作都执行完毕后,回到主线程.
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"group---end");
});
}
//当所有任务执行完成之后,才执行 dispatch_group_notify 中的任务。这里的dispatch_group_enter、dispatch_group_leave 组合,其实等同于dispatch_group_async。
九、GCD 信号量:dispatch_semaphore
GCD 中的信号量是指 Dispatch Semaphore,是持有计数的信号。类似于过高速路收费站的栏杆。可以通过时,打开栏杆,不可以通过时,关闭栏杆。在 Dispatch Semaphore 中,使用计数来完成这个功能,计数小于 0 时等待,不可通过。计数为 0 或大于 0 时,计数减 1 且不等待,可通过。
Dispatch Semaphore 提供了三个方法:
1.dispatch_semaphore_create:创建一个 Semaphore 并初始化信号的总量
2.dispatch_semaphore_signal:发送一个信号,让信号总量加 1
3.dispatch_semaphore_wait:可以使总信号量减 1,信号总量小于 0 时就会一直等待(阻塞所在线程),否则就可以正常执行。
Dispatch Semaphore 在实际开发中主要用于:
1.保持线程同步,将异步执行任务转换为同步执行任务
2.保证线程安全,为线程加锁
1、Dispatch Semaphore 线程同步
/**
* semaphore 线程同步
*/
- (void)semaphoreSync {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"semaphore---begin");
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);//1.创建一个 Semaphore 并初始化信号的总量
__block int number = 0;
dispatch_async(queue, ^{
// 追加任务 1
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
number = 100;
dispatch_semaphore_signal(semaphore);//3.发送一个信号,让信号总量加 1
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//2.可以使总信号量减 1,信号总量小于 0 时就会一直等待(阻塞所在线程),否则就可以正常执行。
NSLog(@"semaphore---end,number = %zd",number);
}
//这是一个异步线程,没加信号量的话,应该是number = 0;加上信号量 number = 100;
1.semaphore 初始创建时计数为 0。
2.异步执行 将 任务 1 追加到队列之后,不做等待,接着执行 dispatch_semaphore_wait 方法,semaphore 减 1,此时 semaphore == -1,当前线程进入等待状态。
3.然后,异步任务 1 开始执行。任务 1 执行到 dispatch_semaphore_signal 之后,总信号量加 1,此时 semaphore == 0,正在被阻塞的线程(主线程)恢复继续执行。
4.最后打印 semaphore---end,number = 100。
2、 线程安全(使用 semaphore 加锁)
/**
* 线程安全:使用 semaphore 加锁
* 初始化火车票数量、卖票窗口(线程安全)、并开始卖票
*/
- (void)initTicketStatusSave {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"semaphore---begin");
semaphoreLock = dispatch_semaphore_create(1);
self.ticketSurplusCount = 50;
// queue1 代表北京火车票售卖窗口
dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_SERIAL);
// queue2 代表上海火车票售卖窗口
dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_SERIAL);
__weak typeof(self) weakSelf = self;
dispatch_async(queue1, ^{
[weakSelf saleTicketSafe];
});
dispatch_async(queue2, ^{
[weakSelf saleTicketSafe];
});
}
/**
* 售卖火车票(线程安全)
*/
- (void)saleTicketSafe {
while (1) {
// 相当于加锁
dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);
if (self.ticketSurplusCount > 0) { // 如果还有票,继续售卖
self.ticketSurplusCount--;
NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]);
[NSThread sleepForTimeInterval:0.2];
} else { // 如果已卖完,关闭售票窗口
NSLog(@"所有火车票均已售完");
// 相当于解锁
dispatch_semaphore_signal(semaphoreLock);
break;
}
// 相当于解锁
dispatch_semaphore_signal(semaphoreLock);
}
}
//在考虑了线程安全的情况下,使用 dispatch_semaphore机制之后,得到的票数是正确的,没有出现混乱的情况。我们也就解决了多个线程同步的问题。
十、GCD 定时器:dispatch_source
用UIButton的类别实现一个注册倒计时的功能
- (void)countDownFromTime:(NSInteger)startTime title:(NSString *)title unitTitle:(NSString *)unitTitle mainColor:(UIColor *)mColor countColor:(UIColor *)color {
__weak typeof(self) weakSelf = self;
// 剩余的时间
__block NSInteger remainTime = startTime;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 每秒执行一次
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
// 子线程(queue)执行event_handler
dispatch_source_set_event_handler(timer, ^{
if (remainTime <= 0) { // 倒计时结束
dispatch_source_cancel(timer);
// 主线程更新UI
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.backgroundColor = mColor;
[weakSelf setTitle:title forState:UIControlStateNormal];
weakSelf.enabled = YES;
});
} else {
NSString *timeStr = [NSString stringWithFormat:@"%ld", remainTime];
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.backgroundColor = color;
[weakSelf setTitle:[NSString stringWithFormat:@"%@%@",timeStr,unitTitle] forState:UIControlStateDisabled];
weakSelf.enabled = NO;
});
remainTime--;
}
});
dispatch_resume(timer);
}

浙公网安备 33010602011771号