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);