(一)GCD的API
1.DispatchQueue
(1).DispatchQueue
先进先出FIFO
(2).SerialQueue
SerialQueue串行队列,该队列中的内容需等待上一个任务结束后,才会执行下一个.SerialQueue可以生成多个线程,但是要谨慎使用
(3).ConcurrentQueue
ConcurrentQueue并行队列,该队列中的内容无需等待上一个任务结束,才会执行下一个.ConcurrentQueue无论生成多少线程,XNU内核只使用有效管理的线程,因此不会发生SerialDispatchQueue的那些问题
(4).block的归属问题(暂未解决)
2020.09.17更新,gcd是函数不是方法,他属于系统,并不属于某个类,所以不会有持有关系,因此无需担心循环引用的问题.
2.dispatch_queue_create
(1).SerialDispatchQueue生成个数的问题
如果创建5个SerialDispatchQueue的队列,每个队列里面都有一个任务,那么这5个Queue会并行执行,5个任务会同时进行.每一个Queue都是一个线程,不要大量的创建Queue会引起资源的竞争等问题,实际使用时要注意.
(2). Serial Dispatch Queue使用时机
多个任务更新相同资源导致数据竞争时使用Serial Dispatch Queue.
(3).dispatch_queue_create介绍
dispatch_queue_create(const char * _Nullable label, dispatch_queue_attr_t _Nullable attr)
1.第一个参数Queue的名称,当该模块出现崩溃时这个名称会出现在崩溃生成的Crashlog中,所以起好名字是有必要的.
2.第二个参数指定的是队列的类型,设置为NULL默认是SerialDispatchQueue,也可以手动添加DISPATCH_QUEUE_SERIAL,DISPATCH_QUEUE_CONCURRENT
3.返回值是dispatch_queue_t类型
5.补充一下,现在基本没人会用的iOS6之前需要手动释放,如果没使用ARC也需要手动释放,具体不详述
3.Main DIspatch Queue/Global Dispatch Queue
(1).Main Dispatch Queue
1.是在主线程RunLoop中执行的Queue
2.属于Serial Dispatch Queue队列
(2).Global Dispatch Queue
1.属于Concurrent Dispatch Queue
2.Global Dispatch Queue有四个优先级,所以在队列中添加任务时应选择与处理内容对应的执行优先级
High Priority
Default Priority
Low Priority
Background Priority
(3).获取Queue
dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();
dispatch_queue_t globalDispatchQueueHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0);
dispatch_queue_t globalDispatchQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_queue_t globalDispatchQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0);
dispatch_queue_t globalDispatchQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0);
4.dispatch_set_target_queue
(1).意义
这个函数的意义在于修改自己创建的Queue的执行优先级
(2).调整自己创建的queue的优先级
dispatch_queue_create函数生成的Dispatch Queue不管是Serial还是Concurrent,都使用与默认优先级Global Dispatch Queue相同执行优先级的线程. 意思是他们的优先级相当于Global的默认优先级. 而变更生成的Dispatch Queue的执行优先级要使用dispatch_set_target_queue函数
举例:在后台执行动作处理的Serial Dispatch Queue的生成方法
dispatch_queue_t serialDispatchQueue = dispatch_queue_create("com.dong.serial",DISPATCH_QUEUE_SERIAL);
dispatch_queue_t globalDispatchQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0);
dispatch_set_target_queue(serialDispatchQueue,globalDispatchQueueBackground);
//通过dispatch_set_target_queue函数将Serial的执行优先级调为background
(3).相关参数
第一个参数是需要变更优先级的queue,第二个参数是需要变更的目标.
第一个参数如果指定Main Dispatch Queue或者Global Dispatch Queue,会出现不可预知的情况
(4).操作多个Serial队列
如果对多个Serial Dispatch Queue使用dispatch_set_target_queue函数指定目标为某一个Serial Dispatch Queue,那么原先本应并行执行的多个Serial Dispatch Queue,在目标Serial Dispatch Queue上只能同时执行一个处理,即变成了串行队列
举例如下
dispatch_queue_t serialQueue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t serialQueue1 = dispatch_queue_create("serial1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t serialQueue2 = dispatch_queue_create("serial2", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t serialQueue3 = dispatch_queue_create("serial3", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t serialQueue4 = dispatch_queue_create("serial4", DISPATCH_QUEUE_SERIAL);
dispatch_set_target_queue(serialQueue1, serialQueue);
dispatch_set_target_queue(serialQueue2, serialQueue);
dispatch_set_target_queue(serialQueue3, serialQueue);
dispatch_set_target_queue(serialQueue4, serialQueue);
dispatch_async(serialQueue, ^{
for (int m = 0; m < 5; m ++) {
NSLog(@"1");
}
});
dispatch_async(serialQueue1, ^{
for (int m = 0; m < 5; m ++) {
NSLog(@"2");
}
});
dispatch_async(serialQueue2, ^{
for (int m = 0; m < 5; m ++) {
NSLog(@"3");
}
});
dispatch_async(serialQueue3, ^{
for (int m = 0; m < 5; m ++) {
NSLog(@"4");
}
});
dispatch_async(serialQueue4, ^{
for (int m = 0; m < 5; m ++) {
NSLog(@"5");
}
});
//打印的结果
1111122222333334444455555
5.dispatch_after(延时处理)
(1).dispatch_after
dispatch_after并不是在指定时间后执行,而只是在指定时间后追加到相应的Dispatch Queue,根据队列中其他任务执行的快慢或者其他影响会有一定的延时
(2).相关参数
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW,(int64_t)3*NSEC_PER_SEC);
dispatch_after(time,dispatch_get_main_queue(),^{
NSLog(@"三秒后将这个NSLog添加到主队列执行");
})
第一个参数是时间,具体为多长时间后加入队列.
第二个参数是将任务添加到的目标队列
DISPATCH_TIME_NOW(当前时间)
NSEC_PER_SEC(一秒有多少纳秒,单位秒),NSEC_PER_MSEC(一毫秒有多少纳秒,单位毫秒),USEC_PER_SEC(一秒有多少微妙,这个特殊,单位是毫秒),NSEC_PER_USEC(一微妙有多少纳秒,单位微妙)
时间的表达方式
1秒的表达方式
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW,(int64_t)1*NSEC_PER_SEC);
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW,(int64_t)1000* USEC_PER_SEC);
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW,(int64_t)1000* NSEC_PER_MSEC);
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW,(int64_t)1000000* NSEC_PER_USEC);
6.Dispatch Group
(1).功能
执行完多个任务后执行某一个操作,当group中的任务执行完的时候才会执行dispatch_group_notify里面的任务
//queue是Global他是一个并行的队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_group_t group = dispatch_group_creat();
dispatch_group_async(group,queue,^{
NSLog(@"blk1");
});
dispatch_group_async(group,queue,^{
NSLog(@"blk2");
});
dispatch_group_async(group,queue,^{
NSLog(@"blk3");
});
dispatch_group_notify(group,dispatch_get_main(),^{
NSLog(@"等待上面的所有任务完成才会执行这段代码");
})
//同时也可以使用dispatch_group_wait()函数,但是会阻碍主线程
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
bool result = dispatch_group_wait(group,DISPATCH_TIME_NOW);
if(result == 0){
NSLog(@"group内的任务全部结束");
}else{
NSLog(@"group内的任务未全部结束");
}
7.dispatch_barrier_async
(1).作用
dispatch_barrier_async和dispatch_queue_create函数生成的Concurrent DIspatch Queue一起使用. dispatch_barrier_async函数会阻止队列中的其他未开始的任务开始执行.并且会等待队列中已经开始执行的任务结束,当已经开始的任务全部结束后,会追加dispatch_barrier_async函数的任务到当前队列,此时的队列不会执行其他操作,当dispatch_barrier_async函数的任务结束后queue会恢复到并行执行,堵塞了这个队列但是没堵塞线程,当函数里面的内容的执行完毕后才会继续执下面的内容
举例
__block int m = 10;
void(^blk_for_reading)(void) = ^{
NSLog(@"%d",m);
};
void(^blk_for_writing)(void) = ^{
//执行这个函数的时候,下面的几个任务是不会动的当执行完这个任务后,后面的才会开始执行
sleep(2);
m = 12;
};
dispatch_queue_t queue = dispatch_queue_create("com.dong.barrier",DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue,blk_for_reading);
dispatch_async(queue,blk_for_reading);
dispatch_async(queue,blk_for_reading);
dispatch_async(queue,blk_for_reading);
dispatch_async(queue,blk_for_reading);
dispatch_barrier_async(queue,blk_for_writing);
dispatch_async(queue,blk_for_reading);
dispatch_async(queue,blk_for_reading);
dispatch_async(queue,blk_for_reading);
dispatch_async(queue,blk_for_reading);
dispatch_async(queue,blk_for_reading);
dispatch_async(queue,blk_for_reading);
8.dispatch_async,dispatch_sync
(1).简介
dispatch_async:异步线程不会堵塞线程,举例就是说dispatch_async中的内容在执行的时候,不会影响后面的内容的执行.
dispatch_sync:同步线程,执行dispatch_sync里面的block执行完毕之前线程不会进行下一步
(2).实现简单的dispatch_group_wait()
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
NSLog(@"11");
dispatch_sync(queue,^{
NSLog(@"22");
});
NSLog(@"33");
//打印顺序永远是11,22,33
(3).死锁
//test是在主队列中
-(void)test{
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"11");
dispatch_sync(queue,^{
NSLog(@"22");
})
NSLog(@"33");
}
上面的代码是典型的死锁,只能打印"11",其他的上面都不会打印,为什么呢?答案是问题出在队列身上,首先要记得最开始说的队列是FIFO先进先出的规则,在主队列Main Queue中test是先进去的,这个时候dispatch_sync也加入到这个队列,dispatch_sync需要等待test执行完才能执行,但是test在等待dispatch_sync执行,于是相互等待造成了死锁的状况.
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue,^{
dispatch_sync(queue,^{
//这也是死锁
})
})
外层的dispatch_async在主队列中相当于之前的test方法,所以还是死锁.
破解死锁的关键就是不要让队列造成相互等待的局面
再来一个死锁
dispatch_queue_t queue = dispatch_queue_create("com.dong.serial",DISPATCH_QUEUE_SERIAL);
dispatch_async(queue,^{
dispatch_sync(queue,^{
})
})
上面的又是一个死锁,外面的等里面,里面的等外面,破解的话只需要将模式改为DISPATCH_QUEUE_CONCURRENT即可
如果对于死锁还有什么不理解的推荐一篇文章,写的很好解释的很到位可以参考一下
理解GCD死锁
9.dispatch_apply
(1).dispatch_apply函数是dispatch_sync函数和Dispatch Group的关联API.该函数按指定的次数将指定的block追加到指定的Dispatch Queue中,并等待全部处理执行结束,相当于for in遍历循环
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSArray * arr = @[@"天",@"地",@"之",@"间",@"有",@"杆",@"称",@"那",@"秤",@"坨"];
// int m = sizeof(&arr);
// int n = sizeof(arr[0]);
dispatch_apply(arr.count, queue, ^(size_t index) {
NSLog(@"%@",arr[index]);
});
NSLog(@"done");
使用举例
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_async(queue,^{
dispatch_apply(20,queue,^(size_t index){
NSLog(@"%zu",index);
});
dispatch_async(dispatch_get_main_queue(),^{
//更新主线程
NSLog(@"会不会死锁?当然不会第一不在一个队列,第二不是同步线下不需要等待");
});
});
10.dispatch_suspend / dispatch_resume
(1).作用
dispatch_suspend(queue):暂停执行队列里面添加的任务,任务中已经开始执行的不受影响,继续执行.
dispatch_resume(queue):继续执行已暂停队列中任务.
(2).相关代码
//(dispatch_suspend崩溃闪退的实例)
dispatch_queue_t queue = dispatch_queue_create("com.dong.suspend",DISPATCH_QUEUE_SERIAL);
dispatch_async(queue,^{
NSLog(@"1");
sleep(1);
});
dispatch_async(queue,^{
NSLog(@"2");
});
dispatch_async(queue,^{
NSLog(@"3");
});
dispatch_async(queue,^{
NSLog(@"4");
});
NSLog(@"5");
dispatch_suspend(queue);
按推理是只能打印5然后再打印1或者1,2或者其他,实际上结果也确实这样,但是然后崩溃,后来查阅资料了解了一下是因为dispatch_suspend()和dispatch_resume()需要配对使用.这个时候如果queue是全局的是不会崩溃的,但是在释放控制器的时候依然会崩溃,所以注意他们要成对出现.崩溃的原因是当处于dispatch_suspeng状态时queue是无法释放的.
11.Dispatch Semaphore
(1).简介
dispatch_semaphore相当于一种锁的机制,比如购买火车票的时候,好多请求同时进入,此时使用dispatch_semaphore进行信号量的操作,可以控制请求,一个一个的操作,不至于造成一张票卖了两次的情况.举例说明
//创建dispatch_semaphore,此时设置的信号量的值是1
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
//dispatch_semaphore_wait()函数会使信号量减1,在执行wait函数前信号值大于0那么不会等待,如果在执行wait函数前信号值为0则等待.
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
//然后在这个位置进行操作,此时dispatch_semaphore_wait会阻止任何想进入这一步的操作,直到这一步执行完毕进行下面的signal加1的操作.
dispatch_semaphore_signal(semaphore);
注意
1.信号量初始值不能为负数
2.信号量同时也代表了可以同时进行的线程数,如果为1意味着最多只能有一个线程执行,2的话意味着可以有两条进入执行.
3.dispatch_semaphore函数一般是先减后加的操作就是先wait再signal
(2.)举例代码
//模拟的是网络请求
- (void)dispatchSemaphore{
//w代表的是同时可以进行几个请求
int w = 3;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(w);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int m = 0; m < 10; m ++) {
long d = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"d:%ld",d);
[self fetchNetWorkCompletion:^(NSString * s) {
NSLog(@"%@",s);
dispatch_semaphore_signal(semaphore);
}];
NSLog(@"%d",m);
}
});
NSLog(@"111");
}
- (void)fetchNetWorkCompletion:(void(^)(NSString *))completion{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2);
completion(@"成功");
});
}
12.dispatch_once
+(instancetype)dispatchOnce{
static NSObject * object;
static dispatch_once_t onceToken;
if(!object){
object = [[NSObject alloc]init];
}
return object;
}
13.GCD实现
Dispatch Queue 通过结构体和链表,被实现为FIFO队列.FIFO队列管理是通过dispatch_async等函数所追加的Block.
14.dispatch_block_t
使用方法举例
dispatch_queue_t queue = dispatch_queue_create("com.dong.block",DISPATCH_QUEUE_SERIAL);
dispatch_block_t block1 = dispatch_block_create(DISPATCH_BLOCK_BARRIER,^{
NSLog(@"开始了1");
sleep(6);
NSLog(@"结束了1");
});
dispatch_block_t block2 = dispatch_block_create(DISPATCH_BLOCK_BARRIER,^{
NSLog(@"开始了2");
});
dispatch_async(queue,block1);
dispatch_async(queue,block2);
sleep(1);
dispatch_block_cancel(block1);
dispatch_block_cancel(block2);
网友评论