1.GCD简介
GCD(Grand Central Dispatch)是系统基于C封装的多线程编程技术。它会自动管理线程的生命周期(创建线程、调度任务、销毁线程)。结合block使用,方便灵活。因为GCD偏向系统低层。所以使用GCD,多线程编程的效率会更高。而且使用起来也很方便,系统都为我们封装好了。
导入库?
使用GCD不需要手动导入任何框架。GCD使用的是一个系统库libdispatch,因为系统代码也在使用这个库,所以不需要我们导入。
2.创建与获取队列
1.串行和并发
串行:意思就是一串一串的执行,队列中的任务一个一个的执行,只需要创建一个线程就够了。
并发:队列中的任务是并发执行的,也就是一起执行的,所以需要创建多个线程执行任务。
2.串行队列
创建串行队列
// 串行队列 参数:1.唯一标识符 2.NULL和DISPATCH_QUEUE_SERIAL都表示串行队列
dispatch_queue_t serial_queue = dispatch_queue_create("com.xiaoleilei.serial_queue", DISPATCH_QUEUE_SERIAL);
3.并发队列
创建并发队列
// 并发队列 参数:1.唯一标识符 2.DISPATCH_QUEUE_CONCURRENT
dispatch_queue_t concurrent_queue = dispatch_queue_create("com.xiaoleilei.concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
4.全局队列
全局队列是在全局都可以获取的一个并发队列,不需要创建,只需要获取就行了,全局队列有四中,按优先级不同获取。
- DISPATCH_QUEUE_PRIORITY_HIGH
- DISPATCH_QUEUE_PRIORITY_DEFAULT
- DISPATCH_QUEUE_PRIORITY_LOW
- DISPATCH_QUEUE_PRIORITY_BACKGROUND
获取全局队列
// 全局队列(并发队列)参数:1.优先级 2.填0
dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
5.主队列
主队列是一个在全局都可以获取的串行队列,该队列中的任务是在主线程中执行的。一般会将一些在主线程中执行的任务加到主队列中。
获取主队列
// 主队列 (串行队列)
dispatch_queue_t main_queue = dispatch_get_main_queue();
3.提交任务
1.同步提交
dispatch_sync同步添加任务到队列中
// 同步提交 参数1:创建的队列 参数2:block{执行的任务}
dispatch_sync(serial_queue, ^{
//任务
});
注意: 同步提交不管提交到什么队列,不会开辟新线程,在当前线程中执行。同步提交任务到当前队列 会造成线程死锁。
dispatch_sync(main_queue, ^{
NSLog(@"任务执行中。。。。");
});
NSLog(@"同步提交完成");
// 1.dispatch_sync完成后会执行输出
// 2.dispatch_sync要等待block完成后才能继续执行 此时主线程被阻塞
// 3.执行block中的任务,要在主线程中执行
// 主线程在做同步提交任务 并且等待提交的block任务完成
// block中任务的执行应该在主线程执行,主线程在等待提交的任务完成才能执行block中的任务
2.异步提交
dispatch_async异步添加任务到队列中
//参数1:创建的队列 参数2:block{执行的任务}
dispatch_async(serial_queue, ^{
NSLog(@"异步提交");
});
4.监测队列中任务执行完成
如果想在队列中所有的任务执行完成后再做某种操作,在串行队列中,可以把该操作放到最后一个任务执行,但是在并行队列中怎么做呢?这就有dispatch_group成组操作。
// 监测并发队列中任务执行完成
NSLog(@"dispatch_group");
//获取全局队列,全局队列是并发队列
dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 创建group
dispatch_group_t group = dispatch_group_create();
// 向group中添加任务
dispatch_group_async(group, global_queue, ^{
NSLog(@"任务1正在执行。。。。");
//让线程睡5秒钟,模拟正在执行任务
[NSThread sleepForTimeInterval:5];
});
dispatch_group_async(group, global_queue, ^{
NSLog(@"任务2正在执行。。。。");
//让线程睡5秒钟,模拟正在执行任务
[NSThread sleepForTimeInterval:5];
});
// 队列中的任务完成后通知
dispatch_group_notify(group, global_queue, ^{
NSLog(@"事情干完了");
});
5.线程间的通信(信号量)
GCD中有3个信号量有关的操作:
dispatch_semaphore_create 信号量创建
dispatch_semaphore_wait 信号量等待 wait会阻塞线程并且检测信号量的值,直到信号量值大于0才会开始往下执行,同时对信号量执行-1操作.
dispatch_semaphore_signal 发送通知 +1 如果+1前信号量值小于0直接唤醒线程继续执行
模拟场景:控制并发队列中并发数量为5.
NSLog(@"dispatch_semaphore");
//创建一个并发队列
dispatch_queue_t concurrent_queue = dispatch_queue_create("com.xiaoleilei.semaphore", DISPATCH_QUEUE_CONCURRENT);
//创建信号量 参数为5
dispatch_semaphore_t sema = dispatch_semaphore_create(5);
//提交一百个任务。让他们五个五个的并发
for (int i=0; i<100; i++)
{
dispatch_async(concurrent_queue, ^{
// 等待 wait会阻塞线程并且检测信号量的值,直到信号量值大于0才会开始往下执行,同时对信号量执行-1操作.
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
NSLog(@"第%d个任务执行",i);
//模拟任务执行了五秒
[NSThread sleepForTimeInterval:5];
// 发送通知 +1 如果+1前的值信号量值小于0直接唤醒线程继续执行
dispatch_semaphore_signal(sema);
});
}
画了一个图(字有点丑TooT):
6.dispatch_once
dispatch_once 是用来保证某个代码块在整个程序生命周期只执行一次。可以用来完成单例类的实现。
[self testDispatchOnce];
[self testDispatchOnce];
[self testDispatchOnce];
[self testDispatchOnce];
[self testDispatchOnce];
[self testDispatchOnce];
[self testDispatchOnce];
- (void)testDispatchOnce
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"testDispatchOnce");
});
}
运行结果:
7.dispatch_barrier_async
在并发队列中,使用dispatch_barrier_async添加的任务,在执行此任务时,队列中的其他任务不执行,当此任务执行完毕后,队列中的其他任务才开始执行。
相当于,食堂有四个窗口打饭,我往那一站,四个窗口都为我打饭,等我把饭打完了,其他人才接着打饭。
注意:要使用自己创建的并发队列,如果使用系统的并发队列的话,是没有效果的。用起来就相当于dispatch_async(异步提交)。
//创建一个并行队列
dispatch_queue_t concurrent_queue_barrier = dispatch_queue_create("com.xiaoleilei.barrier", DISPATCH_QUEUE_CONCURRENT);
//提交任务1
dispatch_async(concurrent_queue_barrier, ^{
NSLog(@"任务1开始执行啦");
[NSThread sleepForTimeInterval:5];
NSLog(@"任务1执行完啦");
});
//提交任务2
dispatch_async(concurrent_queue_barrier, ^{
NSLog(@"任务2开始啦");
[NSThread sleepForTimeInterval:5];
NSLog(@"任务2执行完啦");
});
// dispatch_barrier_async
//插队
dispatch_barrier_async(concurrent_queue_barrier, ^{
NSLog(@"插队的任务3开始执行啦");
[NSThread sleepForTimeInterval:10];
NSLog(@"插队的任务3执行完啦");
})
//提交任务4
dispatch_async(concurrent_queue_barrier, ^{
NSLog(@"任务4开始执行啦");
[NSThread sleepForTimeInterval:5];
NSLog(@"任务4执行完啦");
});
//提交任务5
dispatch_async(concurrent_queue_barrier, ^{
NSLog(@"任务5开始执行啦");
[NSThread sleepForTimeInterval:5];
NSLog(@"任务5执行完了");
});
运行结果:
7.dispatch_apply
dispatch_apply的功能是,将任务指定次数添加到并发队列中。
dispatch_apply是同步的,他会阻塞当前线程,知道队列中的循环任务执行完毕。
//创建并发队列
dispatch_queue_t concurrent_queue_apply = dispatch_queue_create("com.xiaoleilei.apply", DISPATCH_QUEUE_CONCURRENT);
//将指定次数的任务添加到队列中
//参数1.循环次数 参数2.队列 size_t后面的参数是循环次数(无符长整型)
dispatch_apply(10, concurrent_queue_apply, ^(size_t idx) {
NSLog(@"任务执行第%ld", idx);
[NSThread sleepForTimeInterval:5];
NSLog(@"%@", [NSThread currentThread]);
NSLog(@"任务执行第%ld完毕-----", idx);
});
//当任务的循环次数执行完毕才唤醒当前线程。
NSLog(@"所有循环任务执行完毕");
运行结果:
8.dispatch_after&&dispatch_time
dispatch_after&&dispatch_time实现延迟函数
// 构建一个时间 参数1.什么时候 参数2.增量
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC));
NSLog(@"开始");
// 实现延迟函数
dispatch_after(time, dispatch_get_main_queue(), ^{
NSLog(@"延迟10秒");
});
运行结果: