有时候我们在开发过程中,会有这样的需求,a任务开始执行的前提是b任务执行完成,c任务开始执行需要等a、b两个异步任务完成,即a依赖于b,c又依赖a,这种需求我们可以使用的GCD来处理。
//创建分组
dispatch_group_t group =dispatch_group_create();
//创建队列
dispatch_queue_t queue =dispatch_queue_create("queue",DISPATCH_QUEUE_CONCURRENT);
//往分组中添加任务
dispatch_group_async(group, queue, ^{
[NSThreadsleepForTimeInterval:2];//模拟耗时操作
NSLog(@"11111 %@", [NSThreadcurrentThread]);
});
//往分组中添加任务
dispatch_group_async(group, queue, ^{
[NSThreadsleepForTimeInterval:1];//模拟耗时操作
NSLog(@"2222 %@", [NSThreadcurrentThread]);
});
//分组中任务完成以后通知该block执行
dispatch_group_notify(group, queue, ^{
NSLog(@"完成 %@", [NSThreadcurrentThread]);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"通知主线程刷新UI %@", [NSThreadcurrentThread]);
});
});
执行结果:
2017-10-24 11:33:40.426 iOSTest[34497:828682] 2222 0x600000260080>{number = 3, name = (null)}
2017-10-24 11:33:41.426 iOSTest[34497:828660] 11111 0x608000261d80>{number = 4, name = (null)}
2017-10-2411:33:41.427 iOSTest[34497:828660] 完成 0x608000261d80>{number = 4, name = (null)}
2017-10-2411:33:41.427 iOSTest[34497:828365] 通知主线程刷新UI 0x60800007b980>{number = 1, name = main}
这样我们使用group可以实现几个任务之间的依赖关系。
然而有时我们的任务一层一层的嵌套了多个Block,这个时候,就应该使用如下代码方式:
//创建分组
dispatch_group_t group =dispatch_group_create();
//创建队列
dispatch_queue_t queue =dispatch_queue_create("queue",DISPATCH_QUEUE_CONCURRENT);
//往分组中添加任务
dispatch_group_async(group, queue, ^{
void (^task)(void) = ^{
[NSThreadsleepForTimeInterval:2];//模拟耗时操作
NSLog(@"11111 %@", [NSThreadcurrentThread]);
};
dispatch_async(dispatch_get_global_queue(0,0), task);
NSLog(@"11111---- %@", [NSThreadcurrentThread]);
});
//往分组中添加任务
dispatch_group_async(group, queue, ^{
void (^task)(void) = ^ {
[NSThreadsleepForTimeInterval:1];//模拟耗时操作
NSLog(@"2222 %@", [NSThreadcurrentThread]);
};
dispatch_async(dispatch_get_global_queue(0,0), task);
NSLog(@"2222------- %@", [NSThreadcurrentThread]);
});
//分组中任务完成以后通知该block执行
dispatch_group_notify(group, queue, ^{
NSLog(@"完成 %@", [NSThreadcurrentThread]);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"通知主线程刷新UI %@", [NSThreadcurrentThread]);
});
});
执行结果:
2017-10-24 11:44:06.447 iOSTest[34981:881063] 2222------- {number = 4, name = (null)}
2017-10-2411:44:06.447 iOSTest[34981:881046] 11111---- {number = 3, name = (null)}
2017-10-24 11:44:06.448iOSTest[34981:881046] 完成 0x600000071f40>{number = 3, name = (null)}
2017-10-24 11:44:06.450iOSTest[34981:880987] 通知主线程刷新UI 0x60000006c340>{number = 1, name = main}
2017-10-2411:44:07.450 iOSTest[34981:881064] 2222 0x6000000708c0>{number = 5, name = (null)}
2017-10-2411:44:08.452 iOSTest[34981:881049] 11111 0x61000006d5c0>{number = 6, name = (null)}
根据执行结果可以看出,当主线程执行的时候,然而其他两个任务中并没有真正的完成,因为另外两个任务中嵌套了子任务,那问题来了,其他两个任务还没有完成就执行主线程,但是我们需要的是其他两个任务完成才需要执行主线程,别急,group给我们提供了dispatch_group_enter()与dispatch_group_leave()方法来组合运用,值得注意的是,这两个方法一定需要成对使用,要不然有时间又出现一些莫名其妙的bug问题。
代码如下:
//创建分组
dispatch_group_t group =dispatch_group_create();
//创建队列
dispatch_queue_t queue =dispatch_queue_create("queue",DISPATCH_QUEUE_CONCURRENT);
//往分组中添加任务
dispatch_group_enter(group);
dispatch_async(queue, ^{
void (^task)(void) = ^{
[NSThreadsleepForTimeInterval:2];//模拟耗时操作
NSLog(@"11111 %@", [NSThreadcurrentThread]);
dispatch_group_leave(group);
};
dispatch_async(dispatch_get_global_queue(0,0), task);
NSLog(@"11111---- %@", [NSThreadcurrentThread]);
});
//往分组中添加任务
dispatch_group_enter(group);
dispatch_async(queue, ^{
void (^task)(void) = ^ {
[NSThreadsleepForTimeInterval:1];//模拟耗时操作
NSLog(@"2222 %@", [NSThreadcurrentThread]);
dispatch_group_leave(group);
};
dispatch_async(dispatch_get_global_queue(0,0), task);
NSLog(@"2222------- %@", [NSThreadcurrentThread]);
});
//分组中任务完成以后通知该block执行
dispatch_group_notify(group, queue, ^{
NSLog(@"完成 %@", [NSThreadcurrentThread]);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"通知主线程刷新UI %@", [NSThreadcurrentThread]);
});
});
执行结果如下:
2017-10-24 11:48:05.641 iOSTest[35128:902591] 11111---- {number = 3, name = (null)}
2017-10-2411:48:05.641 iOSTest[35128:902608] 2222------- {number = 4, name = (null)}
2017-10-24 11:48:06.644iOSTest[35128:902609] 2222 0x60000006cb00>{number = 5, name = (null)}
2017-10-24 11:48:07.644iOSTest[35128:902593] 11111 0x6080000721c0>{number = 6, name = (null)}
2017-10-24 11:48:07.644iOSTest[35128:902593] 完成 0x6080000721c0>{number = 6, name = (null)}
2017-10-24 11:48:07.645iOSTest[35128:902524] 通知主线程刷新UI 0x61000006e280>{number = 1, name = main}
这样我们想要的得到的结果就实现了。
A,B,C三个任务并发执行,但是C要等A,B执行完成之后再执行。
信号量:
// 创建一个信号,value:信号量
dispatch_semaphore_create(<#long value#>)
// 使某个信号的信号量+1
dispatch_semaphore_signal(<#dispatch_semaphore_t dsema#>)
// 某个信号进行等待或等待降低信号量 timeout:等待时间,永远等待为 DISPATCH_TIME_FOREVER
dispatch_semaphore_wait(<#dispatch_semaphore_t dsema#>, <#dispatch_time_t timeout#>)
正常的使用顺序是先降低然后再提高,这两个函数通常成对使用。
两种方法
-(void)dispatch_group_function1
{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_queue_create("com.dispatch.test", DISPATCH_QUEUE_CONCURRENT), ^{
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"https://www.baidu.com"]];
NSURLSessionDownloadTask *task = [[NSURLSession sharedSession] downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// 请求完成,可以通知界面刷新界面等操作
NSLog(@"第一步网络请求完成");
// 使信号的信号量+1,这里的信号量本来为0,+1信号量为1(绿灯)
dispatch_semaphore_signal(semaphore);
}];
[task resume];
// 以下还要进行一些其他的耗时操作
NSLog(@"耗时操作继续进行");
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
dispatch_group_async(group, dispatch_queue_create("com.dispatch.test", DISPATCH_QUEUE_CONCURRENT), ^{
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"https://www.github.com"]];
NSURLSessionDownloadTask *task = [[NSURLSession sharedSession] downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// 请求完成,可以通知界面刷新界面等操作
NSLog(@"第二步网络请求完成");
// 使信号的信号量+1,这里的信号量本来为0,+1信号量为1(绿灯)
dispatch_semaphore_signal(semaphore);
}];
[task resume];
// 以下还要进行一些其他的耗时操作
NSLog(@"耗时操作继续进行");
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"刷新界面等在主线程的操作");
});
}
2019-02-24 18:19:16.251067+0800 Semaphore[33094:12267734] 耗时操作继续进行
2019-02-24 18:19:16.251071+0800 Semaphore[33094:12267735] 耗时操作继续进行
2019-02-24 18:19:16.549563+0800 Semaphore[33094:12267748] 第一步网络请求完成
2019-02-24 18:19:18.091922+0800 Semaphore[33094:12267737] 第二步网络请求完成
2019-02-24 18:19:18.092222+0800 Semaphore[33094:12267662] 刷新界面等在主线程的操作
设定的信号值为2,先执行两个线程,等执行完一个,才会继续执行下一个,保证同一时间执行的线程数不超过2。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//任务1
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 1");
sleep(1);
NSLog(@"complete task 1");
dispatch_semaphore_signal(semaphore);
});
//任务2
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 2");
sleep(1);
NSLog(@"complete task 2");
dispatch_semaphore_signal(semaphore);
});
//任务3
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 3");
sleep(1);
NSLog(@"complete task 3");
dispatch_semaphore_signal(semaphore);
});
2019-02-24 18:31:02.447769+0800 Semaphore[33286:12284312] run task 1
2019-02-24 18:31:02.447767+0800 Semaphore[33286:12284310] run task 2
2019-02-24 18:31:03.450756+0800 Semaphore[33286:12284312] complete task 1
2019-02-24 18:31:03.450756+0800 Semaphore[33286:12284310] complete task 2
2019-02-24 18:31:03.450997+0800 Semaphore[33286:12284311] run task 3
2019-02-24 18:31:04.454259+0800 Semaphore[33286:12284311] complete task 3