先贴上打印函数:
- (void)p_printThread:(int)index {
NSLog(@"%@-------%d", [NSThread currentThread], index);
}
1. Group
Group作用:在并发异步队列中,等待执行一系列任务后,再执行一个任务。实现方式有两种:notify和wait。
notify示例代码:
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, concurrentQueue, ^{
[self p_printThread:1];
});
dispatch_group_async(group, concurrentQueue, ^{
[self p_printThread:2];
});
dispatch_group_async(group, concurrentQueue, ^{
[self p_printThread:3];
});
dispatch_group_async(group, concurrentQueue, ^{
[self p_printThread:4];
});
// 使用dispatch_group_wait,可以阻塞线程,等待group的任务执行完毕,才能继续执行后续任务
// 使用dispatch_group_notify,不会阻塞线程(group外的线程执行顺序不受影响),而且可以在执行完成group的任务后进行操作
dispatch_group_notify(group, concurrentQueue, ^{
[self p_printThread:0];
});
打印结果:
<NSThread: 0x610000260d80>{number = 4, name = (null)}-------2
<NSThread: 0x608000261400>{number = 5, name = (null)}-------3
<NSThread: 0x618000264f40>{number = 3, name = (null)}-------1
<NSThread: 0x610000260e40>{number = 6, name = (null)}-------4
<NSThread: 0x610000260e40>{number = 6, name = (null)}-------0
wait示例代码:
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for (int i=0; i<4; i++) {
dispatch_group_enter(group);
dispatch_async(concurrentQueue, ^{
[self p_printThread:i+1];
});
dispatch_group_leave(group);
}
// 使用dispatch_group_wait,可以阻塞线程,等待group的任务执行完毕
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_main_queue(), ^{
// 主线程处理
[self p_printThread:0];
});
打印结果:
<NSThread: 0x600000076600>{number = 4, name = (null)}-------2
<NSThread: 0x600000076740>{number = 6, name = (null)}-------4
<NSThread: 0x610000073280>{number = 3, name = (null)}-------1
<NSThread: 0x608000076380>{number = 5, name = (null)}-------3
<NSThread: 0x60000007b240>{number = 1, name = main}-------0
结论:运行多次,任务1、2、3、4的顺序不定,但任务0总是最后一个。应用场景:单界面多请求完成后,主线程刷新界面;notify和wait的区别:wait会阻塞线程,notify不会阻塞线程,较好一点
2. Barrier
Barrier作用:Barrier在并发异步队列中起到承上启下的作用(Barrier就像名字一样,类似一个栅栏把前后的任务隔开了)。Barrier前后任务执行顺序为:Barrier前面的线程(多个的话,仍是并行)--->barrier线程--->barrier后面的线程(多个的话,仍是并行)。需要注意的是:Barrier在全局并发队列不起作用,只有在自己创建的并发队列才起作用。
示例代码:
// barrier在dispatch_get_global_queue创建的并行队列中不起作用,需要使用dispatch_queue_create来创建的并行队列才可以
//dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^(){
[self p_printThread:1];
});
dispatch_async(concurrentQueue, ^(){
[self p_printThread:2];
});
dispatch_async(concurrentQueue, ^(){
[self p_printThread:3];
});
dispatch_barrier_async(concurrentQueue, ^(){
[self p_printThread:0];
});
dispatch_async(concurrentQueue, ^(){
[self p_printThread:4];
});
dispatch_async(concurrentQueue, ^(){
[self p_printThread:5];
});
dispatch_async(concurrentQueue, ^(){
[self p_printThread:6];
});
打印结果:
<NSThread: 0x600000079300>{number = 4, name = (null)}-------2
<NSThread: 0x6000000790c0>{number = 5, name = (null)}-------3
<NSThread: 0x610000076100>{number = 3, name = (null)}-------1
<NSThread: 0x610000076100>{number = 3, name = (null)}-------0
<NSThread: 0x610000076100>{number = 3, name = (null)}-------4
<NSThread: 0x600000079300>{number = 4, name = (null)}-------6
<NSThread: 0x600000078d40>{number = 6, name = (null)}-------5
结论:运行多次后可以看出,Barrier的任务0总是在任务1、2、3之后,而且总是在任务4、5、6之前,但是任务1、2、3的执行顺序不定,任务4、5、6的执行顺序也不定。Barrier的任务就像“栅栏”一样隔开了前后的任务。
3. Semaphore
Semaphore作用:信号量可以用来控制同时访问资源的线程数量:比如系统只有两个资源可以用,有三个线程要访问,那么只能允许两个线程同时访问,第三个线程应当等待资源被释放后再访问。使用信号量可以实现类似于NSOperationQueue里的并发控制:信号数>0,执行任务;信号数<=0,阻塞线程
示例代码:
// 下面的例子只允许3个线程同时执行
dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);// 创建一个信号量,初始值为3
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i=0; i<10; i++) {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);// 计数-1
dispatch_async(concurrentQueue, ^{
[self p_printThread:i];
sleep(5);
dispatch_semaphore_signal(semaphore);// 计数+1
});
}
打印结果:
<NSThread: 0x608000077b80>{number = 5, name = (null)}-------2
<NSThread: 0x610000074fc0>{number = 3, name = (null)}-------0
<NSThread: 0x6000000787c0>{number = 4, name = (null)}-------1
<NSThread: 0x6000000787c0>{number = 4, name = (null)}-------3
<NSThread: 0x610000074fc0>{number = 3, name = (null)}-------4
<NSThread: 0x608000077b80>{number = 5, name = (null)}-------5
<NSThread: 0x6000000787c0>{number = 4, name = (null)}-------7
<NSThread: 0x608000077b80>{number = 5, name = (null)}-------6
<NSThread: 0x610000074fc0>{number = 3, name = (null)}-------8
<NSThread: 0x610000074fc0>{number = 3, name = (null)}-------9
结论:由于信号量的值为3,每次只能同时执行3个线程里的任务
4. NSOperationQueue
NSOperationQueue作用:
1.将NSOperation添加到NSOperationQueue,使其异步执行 === GCD并行异步队列。2.NSOperationQueue可以设置依赖关系 ~= GCD Group notify / GCD Group Wait / GCD Barrier。
3.可以设置最大并发数量(同时执行的线程数) === GCD 信号量
示例代码:
NSOperationQueue *operationQueue = [NSOperationQueue new];
NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{
[self p_printThread:1];
sleep(5);
}];
NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{
[self p_printThread:2];
sleep(5);
}];
NSBlockOperation *blockOperation3 = [NSBlockOperation blockOperationWithBlock:^{
[self p_printThread:3];
sleep(5);
}];
NSBlockOperation *blockOperation4 = [NSBlockOperation blockOperationWithBlock:^{
[self p_printThread:4];
sleep(5);
}];
// 2.NSOperationQueue可以设置依赖关系 ~= GCD Group notify / GCD Group Wait / GCD Barrier
[blockOperation1 addDependency:blockOperation2];
// 3.可以设置最大并发数量(同时执行的线程数) === GCD 信号量
operationQueue.maxConcurrentOperationCount = 1;
[operationQueue addOperations:@[blockOperation1, blockOperation2, blockOperation3, blockOperation4] waitUntilFinished:YES];
打印结果:
<NSThread: 0x600000078300>{number = 3, name = (null)}-------2
<NSThread: 0x600000078300>{number = 3, name = (null)}-------1
<NSThread: 0x60000007aa80>{number = 4, name = (null)}-------3
<NSThread: 0x60000007aa80>{number = 4, name = (null)}-------4
结论:运行多次可以看出,每次只能执行一个线程里的任务(这个类似信号量的控制);虽然任务1、2、3、4的顺序不定,但是任务1总是在任务2的后面(类似group的notify和wait)。对比可以看出NSOperationQueue使用更方便一些,语法也更接近Objective-C。