GCD回顾

GCD的API

DISPATCH_QUEUE_SERIAL / DISPATCH_QUEUE_CONCURRENT

DISPATCH_QUEUE_SERIAL 顺序执行-(适用于操作文件)同步 print: more less
DISPATCH_QUEUE_CONCURRENT 同时执行-异步 print: less more

dispatch_queue_t dq = dispatch_queue_create("com.zhuchen.www", DISPATCH_QUEUE_SERIAL);
dispatch_async(dq, ^{
    NSInteger num = 0;
    for (NSInteger i = 0; i<100000000; i++) {
        num = num+i;
    }
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"%li",num);
    });
});
dispatch_async(dq, ^{
    NSInteger num = 0;
    for (NSInteger i = 0; i<1000000; i++) {
        num = num+i;
    }
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"%li",num);
    });
});

Main Dispatch Queue / Global Dispatch Queue

Main Dispatch Queue 主线程执行 是-- Serial Dispatch Queue
Global Dispatch Queue 是-- Concurrent Dispatch Queue,含有4个优先级
High Priority 高优先级
Default Priority 默认优先级
Low Priority 低优先级
Background Priority 后台优先级

各种Dispatch Queue的获取方法

dispatch_queue_t main = dispatch_get_main_queue();
dispatch_queue_t globalHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)
...

dispatch_set_target_queue(parameter1, parameter2) 1的优先级变更为2的优先级

dispatch_after

延迟执行 -- 延迟加入到Main Dispatch Queue,如果Main Dispatch Queue中有其他任务则会等其他任务完成后才会执行。

dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0/*延迟执行时间*/ * NSEC_PER_SEC));
dispatch_after(delayTime, dispatch_get_main_queue(), ^{
    
});

Dispatch Group

使用Concurrent Dispatch Queue时用。

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, globalQueue, ^{
    NSLog(@"1");
});
dispatch_group_async(group, globalQueue, ^{
    NSLog(@"2");
});
dispatch_group_async(group, globalQueue, ^{
    NSLog(@"3");
});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    NSLog(@"over");
})
//也可以设置等待时间,根据结果决定策略
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0/*延迟执行时间*/ * NSEC_PER_SEC));
long result = dispatch_group_wait(group, delayTime);
if  (result == 0) { //全部完成 } else //未完成

dispatch_group_create 类似 dispatch_queue_create
dispatch_group_async 类似 dispatch_async

dispatch_barrier_async

读完成(全部完成后) -> 写任务 -> (写任务完成后)读任务

dispatch_queue_t dq = dispatch_queue_create("com.zhuchen.www", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(dq, ^{ NSLog(@"reading"); });
dispatch_async(dq, ^{ NSLog(@"reading"); });
dispatch_async(dq, ^{ NSLog(@"reading"); });
dispatch_barrier_async(dq, ^{ NSLog(@"writing"); });
dispatch_async(dq, ^{ NSLog(@"reading"); });
dispatch_async(dq, ^{ NSLog(@"reading"); });
dispatch_async(dq, ^{ NSLog(@"reading"); });

dispatch_sync

同步--不常用,而且容易发生死锁。

dispatch_apply

dispatch_apply函数是dispatch_sync函数和Dispatch Group的关联API。相当于重复任务
dispatch_apply函数会等待里面的任务执行完毕才返回。推荐在dispatch_async函数中非同步使用

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply([array count], queue, ^(size_t index) {
  NSLog(@"%li",index);
});

以上可以代替for循环

dispatch_suspend / dispatch_resume

当追加大量处理到Dispatch Queue时,在追加处理的过程中,有时希望不执行已追加的处理。这些函数对已经执行的处理没有影响。挂起后,追加到Dispatch Queue中但尚未执行的处理会停止执行,恢复后则继续执行。
dispatch_suspend() 函数挂起指定的Dispatch Queue
dispatch_resume() 函数恢复指定的Dispatch Queue

Dispatch Semaphore

当并行执行的处理更新数据时,会产生数据不一致的情况,有时还会crash。虽然使用Serial Dispatch Queue 和 dispatch_barrier_async 函数可避免这类问题,但是这个颗粒度相对较大,所以可以使用颗粒度更细的排他控制dispatch_semaphore_t

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

NSMutableArray *array = [NSMutableArray array];

for (int i=0; i<1000; i++) {
    dispatch_async(queue, ^{
//         dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        [array addObject:@(i)];
//         dispatch_semaphore_signal(semaphore); //执行完计数+1
    });
}

这样输出:会有重复数据。 去掉注释则不会出现重复数据。

dispatch_once

dispatch_once 函数是保证在应用程序执行中只执行一次指定处理的API,多用于单例。通过dispatch_once函数执行的方式可以保证在多线程环境下百分之百安全。

static int initialized = NO;
if (initialized == NO) {
    //初始化
    initialized = YES;
}

//比上边的方式安全。
static dispatch_once_t once;
dispatch_once( &once, ^{
    //初始化
});

Dispatch I/O

在读取大文件时,如果将文件分成合适的大小并使用 Global Dispatch Queue 并列读取会快很多。

//串行异步读取本地文件

- (void)asyncSerialReadData {
    static int serialNumberl = 0;
    NSString *path = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"md"]; //PDF、md、word、txt...

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //根据路径创建I/O
    dispatch_io_t chanel = dispatch_io_create_with_path(DISPATCH_IO_STREAM, [path UTF8String], 0, 0, queue, ^(int error) {
    
    });

    size_t water = 1024;  //1K
    dispatch_io_set_low_water(chanel, water); //单次读取最小值
    dispatch_io_set_high_water(chanel, water); //单次读取最大值
    NSMutableData *totalData = [[NSMutableData alloc] init];

    //读取操作
    dispatch_io_read(chanel, 0, SIZE_MAX, queue, ^(bool done, dispatch_data_t  _Nullable data, int error) {
    
    if (error == 0) { // 单次读取大小
        size_t size = dispatch_data_get_size(data);
      if (size > 0 ) {
        [totalData appendData:(NSData*)data]; //拼接数据
      }
    }
    
    if (done) {
        // 完整读写结果
        NSString *str = [[NSString alloc] initWithData:totalData encoding:NSUTF8StringEncoding];
        NSLog(@"%@", str);
    } else {
      serialNumberl ++;
      NSLog(@"read not done,第%d次读取,在%@线程",serialNumberl,[NSThread currentThread]);
    }
  });
}

并行异步读取本地文件-效率较高

- (void)asyncConcurrentReadData {
    static int concurrentNumberl = 0;
    NSString *path = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"md"];

    dispatch_queue_t queue = dispatch_queue_create("readDataQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();

    dispatch_fd_t fd = open(path.UTF8String, O_RDONLY); //只读

    dispatch_io_t chanel = dispatch_io_create(DISPATCH_IO_RANDOM, fd, queue, ^(int error) {
    close(fd);
    });

    long long fileSize = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil].fileSize;
    size_t offset = 1024;

    NSMutableData *totalData = [[NSMutableData alloc] initWithLength:fileSize];

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    for (off_t currentSize = 0; currentSize <= fileSize; currentSize += offset) {
        dispatch_group_enter(group);
        dispatch_io_read(chanel, currentSize, offset, queue, ^(bool done, dispatch_data_t  _Nullable data, int error) {
      
      if (error == 0) {
        size_t size = dispatch_data_get_size(data);
         if (size > 0) {
                dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            [totalData appendData:(NSData*)data];
            dispatch_semaphore_signal(semaphore);
         }
      }
        
      if (done) {
        dispatch_group_leave(group);
      }else {
            NSLog(@"read not done,第%d次读取,在%@线程",concurrentNumberl,[NSThread currentThread]);
      }
   });
}

    dispatch_group_notify(group, queue, ^{
    NSString *str = [[NSString alloc] initWithData:totalData encoding:NSUTF8StringEncoding];
        str = [str stringByReplacingOccurrencesOfString:@"\0" withString:@""];
        NSLog(@"***%@", str);
    });   
}

GCD 实现

Dispatch Source

Dispatch Source 是BSD系内核惯有功能kqueue的包装。kqueue 是在XNU内核中发生各种事件时,在应用程序编程方执行处理的技术(最优秀)。

Dispatch Source 的种类

DISPATCH_SOURCE_TYPE_DATA_AD        变量增加
DISPATCH_SOURCE_TYPE_DATA_OR        变量 OR
DISPATCH_SOURCE_TYPE_MACH_SEND      MACH 端口发送
DISPATCH_SOURCE_TYPE_MACH_RECV      MACH 端口接受
DISPATCH_SOURCE_TYPE_PROC           检测到与进程相关的事件
DISPATCH_SOURCE_TYPE_SIGNAL         接收信号
DISPATCH_SOURCE_TYPE_TIMER          定时器
DISPATCH_SOURCE_TYPE_VNODE          文件系统有变更
DISPATCH_SOURCE_TYPE_READ           可读取文件映像
DISPATCH_SOURCE_TYPE_WRITE          可写入文件映像

创建

dispatch_source_create(<#dispatch_source_type_t  _Nonnull type#>, <#uintptr_t handle#>, <#unsigned long mask#>, <#dispatch_queue_t  _Nullable queue#>)

实现定时器 _timer 为成员变量,不然会自动释放实现不了重复执行。

_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 2ull * NSEC_PER_SEC);
//将定时器设定为2s后  5s循环一次   允许延迟0s
dispatch_source_set_timer(_timer, time, 5ull * NSEC_PER_SEC, 0);
dispatch_source_set_event_handler(_timer, ^{
    NSLog(@"wake up!");
    //只执行一次-取消定时器
    dispatch_source_cancel(self->_timer);
});

dispatch_source_set_cancel_handler(_timer, ^{
    NSLog(@"canceled!");
});
dispatch_resume(_timer);

你可能感兴趣的:(GCD回顾)