美文网首页
【工具类】iOS dispatch_group_notify并发

【工具类】iOS dispatch_group_notify并发

作者: Allan_野草 | 来源:发表于2018-06-29 15:36 被阅读27次

在实际开发中,应该也遇到过这样的需求:
要通过请求 [接口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

最近项目没那么忙了,以后要多造轮子。。

End

相关文章

网友评论

      本文标题:【工具类】iOS dispatch_group_notify并发

      本文链接:https://www.haomeiwen.com/subject/xptayftx.html