本文源自本人的学习记录整理与理解,其中参考阅读了部分优秀的博客和书籍,尽量以通俗简单的语句转述。引用到的地方如有遗漏或未能一一列举原文出处还望见谅与指出,另文章内容如有不妥之处还望指教,万分感谢 !
队列
这里的队列指执行任务的等待队列,即用来存放任务的队列;
队列是一种特殊的线性表,一般开发中的队列有两种形式:串行队列、并发队列;
GCD
- GCD是用来替代NSTread线程技术, 可以充分利用设备的多核且保证线程安全。
- 开发者可以通过GCD来开启子线程加入到队列中执行任务。
GCD的常用函数
- GCD中有2个用来执行任务的函数
用同步的方式执行任务
void dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);
queue:队列
block:任务
用异步的方式执行任务
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
- 串行队列(
Serial Dispatch Queue
)- 让任务排队执行(一个任务执行完毕后,再执行一按一个任务)
- 采用 FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。
每读取一个任务,则从队列中释放一个任务。队列的结构可参考下图:
串行队列.png//创建串行队列
第一个参数:const char *_Nullable label 队列名 自定义就可以
第二个参数:dispatch_queue_attr_t _Nullable attr 队列标识
DISPATCH_QUEUE_SERIAL:串行队列
dispatch_queue_create("mySerialqueu", DISPATCH_QUEUE_SERIAL);
注意:GCD
创建串行队列,虽然使用了create
方式创建;但这是不需要开发者自己释放的,GCD
内部会进行处理。
和CFUUIDCreate()
这样的CoreFoundation
框架中的C语言API
不同,这是需要调用CFRelease(<#CFTypeRef cf#>)
手动内存管理的.
-主队列
-
主队列由系统在程序启动之初就
自动创建
完成 -
主线程所在的队列就是主队列
-
主队列也是一种特殊的串行队列,特殊是因为
只能获取不能创建
;主队列不接受创建 -
并发队列 (
Concurrent Dipatch Queue
)- 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
- 并发功能只有在异步(
dispatch_async
)函数下才有效
注意
:这里的同时
并不是真正的多条线程同一时间做任务,而是在多条线程中快速的切换执行任务,只不过这个速度非常的快而已。
//获取全局并发队列
DISPATCH_QUEUE_PRIORITY_HIGH 高 2
DISPATCH_QUEUE_PRIORITY_DEFAULT 默认==中 0
DISPATCH_QUEUE_PRIORITY_LOW 底 (-2)
DISPATCH_QUEUE_PRIORITY_BACKGROUND 后台即:最低级别 INT16_MIN
第一个参数:优先级 ,0 == DISPATCH_QUEUE_PRIORITY_DEFAULT
第二个参数:保留供将来使用的值-flags。传递任何非零的值都可能导致-->空返回值。所以最好传0进去
dispatch_get_global_queue(0, 0);
//创建并发队列
第一个参数:const char *_Nullable label 队列名 自定义就可以
第二个参数:dispatch_queue_attr_t _Nullable attr 队列标识
DISPATCH_QUEUE_CONCURRENT :并发队列
dispatch_queue_create("myConcuQueu", DISPATCH_QUEUE_CONCURRENT);
容易混淆的术语
有4个术语比较容易混淆:
同步
、异步
、并发
、串行
-
同步和异步主要影响:
能不能开启新的线程
-
同步
:在当前线程中执行任务,不具备开启新线程的能力;当然这个当前线程包括:主线程、子线程 -
异步
:在新的线程中执行任务,具备开启新线程的能力;就是不一定都能开;比如: 在主队列中就无法开启新线程
-
-
并发和串行主要影响:
任务的执行方式
-
并发
:多个任务并发(同时)执行 -
串行
:一个任务执行完毕后,再执行下一个任务
-
各种队列的执行效果
队列的执行效果@2x.png- 补充:
- 使用
dispatch_sync函数
往当前串行队列
添加任务,就会造成死锁(前提:当前串行队列中的任务还没有执行完); - 就是说判断死锁的条件是:
dispatch_sync(同步)
&当前串行队列
&当前串行队列中的任务还没有执行完
&添加任务
- 使用
可以看出
- 异步并发队列 :会开启新线程并且同时执行多个任务 ,其他情况均不会并发执行任务
- 异步串行队列:会开启一条新线程,但执行任务时一个一个执行
- 其他组合: 全部不会开启新线程,执行任务时一个一个执行
问题1: 以下代码是在主线程执行,输出结果是什么 ? 为什么 ?
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"任务1");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"任务2");
});
NSLog(@"任务3");
}
// dispatch_sync: 立马在当前线程执行任务, 执行完毕才会继续往下执行
输出结果:任务1
原因:
- 队列需要遵循
FIFO
原则,主队列是串行执行任务的;当前viewDidLoad
方法就是一个任务,该任务正在执行中还没有结束; 要想从主队列里取出任务2
执行,就需要等viewDidLoad
执行完才可以;但是dispatch_sync
是同步执行,这就意味着要立刻执行任务2
,且要执行完任务2
才能继续往下执行;你等我我等你,大家都在等这就造成死锁。
把问题1中的dispatch_sync函数改为dispatch_async函数呢 ?
输出结果:任务1、任务3、任务2
原因:dispatch_async不要求立马在当前线程同步执行任务,可以继续执行后面的代码
问题2: 举一个生活中的例子来说明线程死锁
小李和小明到自动取款机取钱;小明先到于是就开始插卡取钱了,这时候小李也到了也想取钱,小李呢脾气很火爆上来就要直接往卡槽里插卡取钱;
小明委屈的说:你让我先把卡退出来你在插卡呀。。。
小李一脸蛮横:哼!!! 我不管我要插卡取钱 !说话就拿卡往卡槽里塞
这时取款机老大爷怒了直接把俩人的卡全吞了 !END
问题3 :以下代码是在主线程执行输出结果是什么 ? 为什么 ?
- (void)interview
{
NSLog(@"执行任务1");
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_SERIAL);
//异步开启子线程加入到串行队列中
dispatch_async(queue, ^{ // 整一个dispatch_async函数调用代号:block0
NSLog(@"执行任务2");
dispatch_sync(queue, ^{ // dispatch_sync函数调用代号:block1
NSLog(@"执行任务3");
});
NSLog(@"执行任务4");
});
NSLog(@"执行任务5");
}
输出结果:执行任务1、执行任务5、执行任务2 ---->死锁在dispatch_sync函数调用
原因:异步串行队列,串行队列是先进先出(FIFO),block0任务先被加入到队列中的,所以必须他先执行完才能执行后面加入的block1;
此时开启的同步任务执行block1,block0要执行完就需要等待block1执行完才能执行后面代码,block1需要等待block0执行完才能够开始执行;
这样就大家谁都没办法执行任务了 !直接阻塞了!
如果问题3中 dispatch_sync() 函数的任务队列是在另一个队列(串行队列、并行队列)中执行;结果如何 ?
答 :不会死锁了!因为不在同一个队列中。
问题4:以下代码是在主线程执行输出结果是什么 ? 为什么 ?
- (void)interview
{
NSLog(@"执行任务1");
//创建并发队列
dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_CONCURRENT);
//把异步任务添加到并发队列中
dispatch_async(queue, ^{ // block0
NSLog(@"执行任务2");
//把同步操作任务添加到并发队列中
dispatch_sync(queue, ^{ // block1
NSLog(@"执行任务3");
});
NSLog(@"执行任务4");
});
NSLog(@"执行任务5");
}
输出结果:执行任务1、执行任务5、执行任务2、执行任务3、执行任务4
原因: 并发队列支持同时执行多个任务,现在往并发队列中添加了两个任务:异步任务、同步任务;他们是不需要等另一个执行完再执行的,执行异步任务的同时也可以执行同步任务;但由于block0是异步所以执行到block1会先让他执行,之后再执行后面的代码。
问题5: 手动创建的全局并发队列和直接获取的全局并发队列有什么区别 ?
- 全局并发队列不论获取多少次,返回的都是同一个队列;即 内存地址相同
- 开发者手动创建的全局并发队列,每次创建出来的都是一个全新的全局并发队列;同名的也依然会创建全新的队列,但不建议这么做;因为有一些处理逻辑会用到这个名字,这样显然会出错 !
网友评论