GCD相关参考
02 Sep 2016
GCD
GCD的一个重要概念是队列,它的核心理念:将长期运行的任务拆分成多个工作单元,并将这些单元添加到dispath queue
中,系统会为我们管理这些dispath queue
,为我们在多个线程上执行工作单元,我们不需要直接启动和管理后台线程。
任务
任务即操作,在GCD
中就是一个block
,任务有两种执行方式,同步执行和异步执行。两者的区别在于是否会阻塞当前线程。
-
同步执行:会阻塞当前线程并等待
block
的任务执行完毕,然后当前线程才会继续往下运行。编译器会根据实际情况优化代码,所以有时候你会发现block其实还在当前线程上执行,并没用产生新线程。 -
异步执行:当前线程会直接往下执行,不会阻塞当前线程。
队列
队列用于存放任务。两种队列:串行队列和并行队列。
-
串行队列:放在其中的任务,
GCD
会FIFO
的取出来一个,然后执行一个,再取下一个执行等等。 -
并行队列:
GCD
会FIFO
的取出来,但是取出来一个就会放到别的线程执行,再取出一个就又会放到另一个线程。取的动作很快,看起来像是一起执行的。GCD
会根据系统资源控制并行的数量,如果任务很多,系统并不会让所有的任务同时执行。
GCD中有三种队列类型:
-
The main queue
: 与主线程功能相同。实际上,提交至main queue
的任务会在主线程中执行。main queue
可以调用dispatch_get_main_queue()
来获得。因为main queue
是与主线程相关的,所以这是一个串行队列。 -
Global queues
: 全局队列是并发队列,并由整个进程共享。进程中存在三个全局队列:高、中(默认)、低三个优先级队列。可以调用dispatch_get_global_queue
函数传入优先级来访问队列。 -
用户队列: 用户队列 (GCD并不这样称呼这种队列, 但是没有一个特定的名字来形容这种队列,所以我们称其为用户队列) 是用函数
dispatch_queue_create
创建的队列,这些队列是串行的。正因为如此,它们可以用来完成同步机制。
死锁
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"任务执行");
});
}
以上代码会产生卡死。原因:dispatch_sync
会阻塞当前线程,也就是主线程;而又要在主线程dispatch_get_main_queue()
中执行NSLog(@"任务执行")
的任务,所以产生卡死。
并不一定只有主线程会产生卡死现象,只不过卡死主线程之任何用户输入都无法响应,所以显得很严重。
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
dispatch_queue_t queue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"1 in %@", [NSThread currentThread]);
dispatch_sync(queue, ^{
NSLog(@"任务执行");
});
NSLog(@"2 in %@", [NSThread currentThread]);
});
}
NSLog(@"任务执行")
永远无法执行,原因:queue
是一个串行队列(DISPATCH_QUEUE_SERIAL
也就是NULL
),dispatch_async(queue...
会在当前线程(主线程)之外再开辟一条线程执行queue
队列,打印了NSLog(@"1 in %@", [NSThread currentThread])
,然后dispatch_sync
会阻塞queue
,同时要在queue
执行NSLog(@"任务执行")
,这样queue
就会死锁。
也就是说,不要在执行串行队列的线程中同步执行同一串行队列。以下两段代码都不会出问题。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"任务执行");
});
});
dispatch_queue_t queue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"1 in %@", [NSThread currentThread]);
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"任务执行");
});
NSLog(@"2 in %@", [NSThread currentThread]);
});
常见用法
dispatch_async
执行全局并发队列:
// 获得全局的并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 将任务添加全局队列中去异步执行
dispatch_async(queue, ^{
NSLog(@"-----任务---%@", [NSThread currentThread]);
});
dispatch_async
执行串行队列:
// 1.创建一个串行队列
dispatch_queue_t queue = dispatch_queue_create("cn.ibloodline.queue", NULL);
// 2.将任务添加到串行队列中 异步 执行
dispatch_async(queue, ^{
NSLog(@"-----任务---%@", [NSThread currentThread]);
});
// 3.非ARC,需要释放创建的队列
// dispatch_release(queue);
dispatch_async
执行主队列:
// 1.主队列(添加到主队列中的任务,都会自动放到主线程中去执行)
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.添加任务到主队列中异步执行(结果还是串行)
dispatch_async(queue, ^{
NSLog(@"-----任务---%@", [NSThread currentThread]);
});
dispatch_sync
执行主队列(死锁):
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"任务执行");
});
});
dispatch_sync
执行全局并发队列:
// 获得全局的并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 将 任务 添加到 全局并发队列 中 同步 执行
dispatch_sync(queue, ^{
NSLog(@"-----任务---%@", [NSThread currentThread]);
});
dispatch_sync
执行串行队列:
// 创建一个串行队列
dispatch_queue_t queue = dispatch_queue_create("cn.ibloodline.queue", NULL);
// 将 任务 添加到 串行队列 中 同步 执行
dispatch_sync(queue, ^{
NSLog(@"-----任务---%@", [NSThread currentThread]);
});