Notes and thoughts from Tony

GCD相关参考

Comments

GCD

GCD的一个重要概念是队列,它的核心理念:将长期运行的任务拆分成多个工作单元,并将这些单元添加到dispath queue中,系统会为我们管理这些dispath queue,为我们在多个线程上执行工作单元,我们不需要直接启动和管理后台线程。

任务

任务即操作,在GCD中就是一个block,任务有两种执行方式,同步执行和异步执行。两者的区别在于是否会阻塞当前线程。

  • 同步执行:会阻塞当前线程并等待block的任务执行完毕,然后当前线程才会继续往下运行。编译器会根据实际情况优化代码,所以有时候你会发现block其实还在当前线程上执行,并没用产生新线程。

  • 异步执行:当前线程会直接往下执行,不会阻塞当前线程。

队列

队列用于存放任务。两种队列:串行队列和并行队列。

  • 串行队列:放在其中的任务,GCDFIFO的取出来一个,然后执行一个,再取下一个执行等等。

  • 并行队列:GCDFIFO的取出来,但是取出来一个就会放到别的线程执行,再取出一个就又会放到另一个线程。取的动作很快,看起来像是一起执行的。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]);   
});