一、GCD的概念:
GCD(Grand Central Dispatch),是有Apple公司开发的一个多核编程的解决方案,用以优化应用程序支持多核处理器,是基于线程模式之上执行并发任务。
二、GCD的优点:
- 利用设备多核进行并行运算
- GCD自动充分使用设备的CPU内核
- GCD自动管理线程的生命周期(线程创建、线程调度、线程销毁)
- 使用简单
三、GCD中的任务和队列
1.任务就是需要GCD处理的操作,即需要多线程处理的代码。
任务的执行分两种:同步执行
、异步执行
同步执行
:只能在当前线程中执行,不具备开启新线程能力,顺序等待、有序执行
异步执行
:具备开启新线程能力,并发执行,不需要等待任务执行结束才能继续其他操作
2.队列就是存放被执行任务的等待队列,是一种特殊的线形表,采用先进先出的原则
队列分两类:
串行队列
、并发队列
,两者主要区别是执行顺序不同及是否有开启新线程能力
串行队列
:每次只会调度一个任务,知道上个任务处理完成才会开始调度处理下一个任 务并发队列
:同时调度分发多个任务出去同时执
3.GCD使用步骤:
- 创建一个任务队列(串行队列、并发队列)
- 将任务添加到创建的等待队列并指定任务的执行类型(异步、同步)
4.队列的创建、获取方法:
-
串行和并行队列
/* * @param label 队列的唯一标识符,用于 DEBUG,可为空。队列的名称推荐使用应用程序 ID 这种逆序全程域名 * @param attr设置为串行队列还是并发队列 */ //dispatch_queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr); // 串行队列的创建方法 dispatch_queue_t queue1 = dispatch_queue_create("q1", DISPATCH_QUEUE_SERIAL); // 并发队列的创建方法 dispatch_queue_t queue2 = dispatch_queue_create("q2", DISPATCH_QUEUE_CONCURRENT); 复制代码
-
主队列(Main Dispatch Queue)
所有主队列的任务会安排至主线程执行
dispatch_queue_t q = dispatch_get_main_queue(); 复制代码
-
全局并发队列(Global Dispatch Queue)
/* * @param identifier 队列优先级级别(默认:DISPATCH_QUEUE_PRIORITY_DEFAULT) * @param flags 队列标识(默认:0) */ //dispatch_get_global_queue(intptr_t identifier, uintptr_t flags); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 复制代码
注意:全局队列异步操作会新建多个线程、操作无序执行
5.任务创建方法:
// 同步执行任务创建方法
dispatch_sync(queue, ^{// 这里放同步执行任务代码});
// 异步执行任务创建方法
dispatch_async(queue, ^{// 这里放异步执行任务代码});
复制代码
dispatch_queue_t queue1 = dispatch_queue_create("q1", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue1, ^{
//操作不会新建线程、操作顺序执行
NSLog(@"同步串行%@",[NSThread currentThread]);
});
dispatch_async(queue1, ^{
//操作需要一个子线程,会新建线程、线程的创建和回收不需要程序员参与,操作顺序执行,“最安全的选择”
NSLog(@"异步串行%@",[NSThread currentThread]);
});
dispatch_queue_t queue2 = dispatch_queue_create("q2", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue2, ^{
//操作不会新建线程、操作顺序执行
NSLog(@"同步并行%@",[NSThread currentThread]);
});
dispatch_async(queue2, ^{
//操作会新建多个线程(有多少任务,就开n个线程执行)、操作无序执行;
//队列前如果有其他任务,会等待前面的任务完成之后再执行;
//场景:既不影响主线程,又不需要顺序执行的操作!
NSLog(@"异步并行%@",[NSThread currentThread]);
});
复制代码
注意:
『主线程』 中调用 『主队列』+『同步执行』 会导致死锁问题。这是因为 主队列中追加的同步任务 和 主线程本身的任务 两者之间相互等待,阻塞了 『主队列』,最终造成了主队列所在的线程(主线程)死锁问题。而如果我们在 『其他线程』 调用 『主队列』+『同步执行』,则不会阻塞 『主队列』,自然也不会造成死锁问题。最终的结果是:不会开启新线程,串行执行任务。
6.信号量(Semaphore)
通过信号量来控制线程同步执行,wait
函数使信号量减1,signal
使信号量加1,当信号量小于0时会阻塞当前线程。还可以通过信号量控制线程的并发量
。
// 创建并发队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("q1", DISPATCH_QUEUE_CONCURRENT);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(concurrentQueue, ^{
sleep(2);
NSLog(@"async1...%@",[NSThread currentThread]);
**dispatch_semaphore_signal(semaphore);**
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(concurrentQueue, ^{
sleep(2);
NSLog(@"async2...%@",[NSThread currentThread]);
**dispatch_semaphore_signal(semaphore);**
});
**dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);**
dispatch_async(concurrentQueue, ^{
sleep(2);
NSLog(@"async3...%@",[NSThread currentThread]);
});
复制代码
7.栅栏(Barrier)
GCD中的栅栏函数作用就是总是让队列中栅栏任务之前的任务先执行,然后执行栅栏任务,接着再执行栅栏之后的任务,以此来达到多线程同步。栅栏函数的功能类似于pthread_rwlock_wrlock
读/写锁。
注意:1.栅栏函数有同步栅栏(dispatch_barrier_sync
)和异步栅栏(dispatch_barrier_async
),区别在于同步栅栏函数会阻塞当前线程,类似于dispatch_sync
,异步栅栏不会阻塞当前线程。2.创建并发队列应该使用dispatch_queue_create
函数手动创建的并发队列,如果传递给此函数的队列是串行队列或全局并发队列,则此函数的行为与dispatch_async函数类似。
dispatch_queue_t concurrentQueue = dispatch_queue_create("q1", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^{
sleep(2);
NSLog(@"async1...%@",[NSThread currentThread]);
});
dispatch_async(concurrentQueue, ^{
sleep(2);
NSLog(@"async1...%@",[NSThread currentThread]);
});
dispatch_barrier_async(concurrentQueue, ^{
sleep(2);
NSLog(@"async2...%@",[NSThread currentThread]);
});
dspatch_async(concurrentQueue, ^{
sleep(2);
NSLog(@"async3...%@",[NSThread currentThread]);
});
dispatch_async(concurrentQueue, ^{
sleep(2);
NSLog(@"async3...%@",[NSThread currentThread]);
});
复制代码