关于gcd

GCD是Grand Central Dispatch 的缩写. 即多线程优化技术. 它可以提供线程安全的队列,串行队列和并行队列,同步和异步执行任务.在队列中, 有很多回调块的执行单位, 完成一个任务后就回调块继续执行.GCD负责线程的创建,管理及释放,我们不用管理者一块.

GCD队列特点:

  1. 不是为了通常的数据存储而设计的
  2. 它没有取消功能, 没有随机访问功能
  3. 使用合理的数据结构来解决问题
  4. 自动的线程创建和回收
  5. 通过块来实现回调, 极大简化代码复杂度

GCD队列类型

串行队列特点: 先进先出, 原子性入队(线程安全), 自动出队.每次只执行一个任务,按入队顺序执行. 并行队列特点: 它保证开始执行的顺序是入队的顺序, 但是任务之间是同时进行的, 不能保证先入队的一定先执行完成

dispatch_async()

异步方法 ,功能类似于 performSelector: onThread: withObject: waitUntilDone:,我们何时使用呢?

dispatch_sync()

同步方法 ,功能类似于 performSelector: onThread: withObject: waitUntilDone:, 我们何时使用呢?

简单的创建队列

dispatch_queue_t queue =dispatch_queue_create("myQueue",DISPATCH_QUEUE_SERIAL);
// DISPATCH_QUEUE_CONCURRENT 并行
// DISPATCH_QUEUE_SIRAIL 串行
dispatch_async(queue, ^{
   // your implemente
});

dispatch barrier

当我们遇到线程安全读写问题的时候,比如访问或这只某些线程不安全的属性,例如 NSMutableArray, 需要使用 dispatch barrier 保证读写唯一性,它相当于一个读写锁, 会在并行队列中保证执行的唯一性,简单的理解就是一旦进入了barrier中, 不论什么队列, 执行barrier的时候, 这个队列暂时变成了串行队列.

dispatch_once

可以保证线程安全的只执行一次,非常有用,适合创建全局使用且创建昂贵的资源

+ (instancetype)sharedManager
{
    static Manager *sharedManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedManager = [[Manager alloc] init];
        // do other things
    });
    return sharedPhotoManager;
}

dispatch_after

延时执行一个任务, 相当于延迟的dispatch_async, 通常使用在main queue上,类似于performSelector: withObject: afterDelay:的功能

int64_t delay = 1;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
         // do your implement
    });

在使用MRC的时候还需要对GCD队列的参数做Retain Release 操作, 在ARC就不需要了, 编译器已经自动添加了.

void performAsyncWithCallback(id object, dispatch_queue_t q) {
    dispatch_retain(q);     // 在MRC的时候需要添加
    dispatch_async(dispatch_queue_create("q", DISPATCH_QUEUE_CONCURRENT), ^{
       
        dispatch_async(q, ^{
            [object callBack];
        });
        dispatch_release(q);   // 在MRC的时候需要添加
    });
}

Dispatch Groups

当我们向监听多个异步任务全部完成的时候,就需要为每个任务添加BOOL点判断位, 这个做非常冗余,不友好, 这时我们需要使用dispatch group 来解决, 它可以同步或异步的在完成组内的所有任务的时候发出通知. API 有两种通知方式:

// 异步执行任务
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 
		
// 创建group									
		dispatch_group_t downloadGroup = 
		dispatch_group_create();								 
        for (NSInteger i = 0; i < 3; i++) {
           // 任务开始进入group
            dispatch_group_enter(downloadGroup);
           // 异步执行任务, 任务使group内部计数加1
	dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
			// 执行出group, 使group 内部计数减1,达到平衡
            dispatch_group_leave(downloadGroup);
        });
        }
        
 // group_wait 是同步方法, 会阻塞当前前程,只有等待group中所有任务完成后 才能往下执行
        dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER);        
        dispatch_async(dispatch_get_main_queue(), ^{
            // some UI operation
        });

// group_notify 异步方法, 异步收到group所有任务完成的通知,不会阻塞当前线程.
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // some UI operation
    });
    });

group也可以监听多个线程的任务,在全部结束是发出通知

dispatch_group_t group = dispatch_group_create();
 dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
      // 并行执行的线程一
 });
 dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
      // 并行执行的线程二
 });
 dispatch_group_notify(group, dispatch_get_global_queue(0,0), ^{
      // 会等待两个线程全结束后, 汇总结果
 });

dispatch_apply

当我们通过 for 循环添加一些任务到队列中的时候,如何才能提高添加速度呢, 我们可以使用dispatch_apply, 它可以异步执行循环内部的实现, 但是它本身是同步的方法, 需要等到循环完全结束后才返回.合适使用它才合适?

dispatch_apply(10, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(size_t i) {
        // 循环执行 10 次
    });

dispatch_source

它可以简单轻松的监听系统事件,用户自定义事件等,它可以在任何的队列上传递并处理回调事件, 也可以执行挂起, 恢复操作, 默认创建后是挂起的.创建dispatch_source 会有很多种要监听的类型,比如:

  1. timer计时器的周期性通知事件
  2. UNIX系统的信号通知
  3. 文件或端口的事件:读数据, 写数据, 文件被删除,移动,修改,重命名等.
  4. 进程的状态事件: 进程退出, 一个信号到达进程, 进程执行或分支
  5. Mach
  6. 用户自定义的事件

创建source 函数原型, 其中 queue为source执行的,type为source类型, queue可以是我们制定的queue, 也可以是DISPATCH_TARGET_QUEUE_DEFAULT, source默认的queue, 这个队列默认是 default priority global queue,默认级别的全局队列.

dispatch_source_create(dispatch_source_type_t type,
	uintptr_t handle,
	unsigned long mask,
	dispatch_queue_t queue);

调用示例

dispatch_source_t readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, DISPATCH_TARGET_QUEUE_DEFAULT);

未完待续: dispatch_set_target_queue dispatch_queue_set_specific dispatch_data_t

具体例子可以参考我的例子 GCD例子 参考 Concurrency Programming Guide 参考1 参考2