【工具类】iOS dispatch_group_notify并发
2018-06-29 本文已影响27人
Allan_野草
在实际开发中,应该也遇到过这样的需求:
要通过请求 [接口C] 获取想要的数据,
但是,必须先通过网络请求 [接口A] 和 [接口B] ,等返回数据,
再把返回数据作为参数,进行C请求
一、
首先,先来一个错误示范,比如第一个想到的dispatch_group_notify
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
dispatch_group_t g_noti = dispatch_group_create();
dispatch_group_async(g_noti, q, ^{// block_g
// 请求A
dispatch_async(q, ^{// block_task
sleep(2);
NSLog(@"task1 complected.");
});
// 执行完这句话,不等block_task执行结束,block_g就算已经执行完毕了
//(已经把block_task放入并发队列)
});
dispatch_group_async(g_noti, q, ^{
// 请求B
dispatch_async(q, ^{
sleep(1);
NSLog(@"task2 complected.");
});
});
dispatch_group_notify(g_noti, dispatch_get_main_queue(), ^{
NSLog(@"task all complected.");
});
打印结果:
task all complected.
task2 complected.
task1 complected.
可惜的是,发现并没有实现想要的效果。因为dispatch_group_notify的block里面,仅仅是执行了异步dispatch,并不是执行耗时的同步网络请求。
那么,像这样多个异步任务执行完成后,触发统一回调,应该怎么处理呢?
二、
为了方便,自己写了个小工具类,源码下面有写。
糅合了 dispatch_group_notify 和 dispatch_semaphore_signal 的用法,
先看它的用法:
@property XIAsynTaskNoti *noti;
// 1 初始化
_noti = [XIAsynTaskNoti notiWithTaskCount:2];
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
dispatch_async(q, ^{// 网络请求A
sleep(2);
NSLog(@"task1 complected.");
// 2 标记网络请求已经完成,并储存返回数据
[_noti signalChildTaskComplected:@"task1" data:@(1)];
});
dispatch_async(q, ^{// 网络请求B
sleep(1);
NSLog(@"task2 complected.");
[_noti signalChildTaskComplected:@"task2" data:@(2)];
});
// 3 当全部请求完成后,在指定线程回调,再去执行网络请求C
[_noti notifyAt:dispatch_get_main_queue() withBlock:^(id data) {
NSLog(@"task all complected.");
NSLog(@"data : %@",data);
/* data =
@{
@"task1" : @1,
@"task2" = @2
}
*/
// C request...
}];
打印结果:
task2 complected.
task1 complected.
task all complected.
data : {
task1 = 1;
task2 = 2;
}
总结一下使用方法:
1 初始化,需要执行几个任务:+notiWithTaskCount:
2 标记一个任务已经完成:-signalChildTaskComplected: data:
3 回调处理 -notifyAt: withBlock:
三、
下面是源码,实现比较简单:
主要在signalChildTaskComplected: 处理了多线程并发控制,参考了ibireme大大 写的YYKit的源码,使用了互斥锁 pthread_mutex
- XIAsynTaskNoti.h
#import <Foundation/Foundation.h>
/**
* 支持多个异步任务执行完成后,触发统一回调
* 类似 dispatch_group_notify
*/
@interface XIAsynTaskNoti : NSObject
/**
* 返回XIAsynTaskNoti对象
* @param count 子任务数量
*/
+(instancetype)notiWithTaskCount:(NSInteger)count;
/**
* 标记一个子任务已经执行完成
* @param data 子任务返回的数据
* @param dKey 区分哪个子任务的数据
*/
-(void)signalChildTaskComplected:(NSString *)dKey data:(id)data;
/**
* 当子任务都执行完成后,触发block
* @param block 回调block。data=@{..},可根据dKey,获取子任务返回的数据
*/
-(void)notifyAt:(dispatch_queue_t)queue withBlock:(void(^)(id data))block;
@end
- XIAsynTaskNoti.m
#import "XIAsynTaskNoti.h"
#import <pthread.h>
typedef void (^NotiRecall)(id data);
@interface XIAsynTaskNoti()
@property(nonatomic, strong) NSMutableDictionary *data;
@end
@implementation XIAsynTaskNoti
{
// condiction
NSInteger _taskCount;
NSInteger _curCount;
// concurrent control
pthread_mutex_t _mutexLock;
// notify
dispatch_queue_t _queue;
NotiRecall _recall;
}
+(instancetype)notiWithTaskCount:(NSInteger)count
{
return [[self alloc] initWithTaskCount:count];
}
-(void)notifyAt:(dispatch_queue_t)queue withBlock:(void (^)(id data))block
{
_queue = queue;
_recall = block;
}
-(void)signalChildTaskComplected:(NSString *)dKey data:(id)data
{
BOOL finish = NO;
BOOL notify = NO;
while (!finish) {
// 并发控制用semaphore/NSLock/@synchronize都可以
if (0==pthread_mutex_trylock(&_mutexLock)) {
finish = YES;
if (dKey&&data) self.data[dKey] = data;
if (++_curCount==_taskCount) notify = YES;
pthread_mutex_unlock(&_mutexLock);
}else {
usleep(10*1000);// 暂时挂起该线程10 ms,等待重试。1ms = 1000us
}
}
if(notify) [self notify];
}
-(void)notify
{
dispatch_async(_queue, ^{
_recall(_data);
});
}
-(instancetype)initWithTaskCount:(NSInteger)count
{
if (self = [self init]) {
_taskCount = count;
}
return self;
}
-(instancetype)init
{
if (self = [super init]) {
pthread_mutex_init(&_mutexLock, NULL);
}
return self;
}
-(void)dealloc
{
pthread_mutex_destroy(&_mutexLock);
}
-(NSMutableDictionary *)data
{
if (!_data) {
_data = [@{} mutableCopy];
}
return _data;
}
@end
最近项目没那么忙了,以后要多造轮子。。