iOS GCD多线程实现并发上传多个本地文件

多线程编程是软件工程是必备的基础技能之一,对于iOS开发,多线程的技术有很多,有最原始的pthread,面向对象的NSThread,NSOperation,以及被广泛开发者喜爱的GCD,之所以这样,因为它相较于其他,更轻量级,以及简便的api接口等等。

今天这里对于GCD的API所有的接口没有过多的描述,主要选择dispatch_group_t(组队列)和dispatch_semaphore_t(信号量)的简单用法及运用他们做了一个简单实用的同时上传多个本地文件的工具进行论述。

一、dispatch_group_t:

1、概念:就是将多个任务同时提交到同一个队列中执行。

并发队列异步执行

2、比如我们有一种需求,就是只有当任务1,2完成了,才能执行3任务,我们有很多方案,比如NSOperation的队列依赖,我们下面要介绍的dispatch_semaphore_t(信号量),还有dispatch_barrier(栈栏)等等,这里要将的是dispatch_group_notify:

dispatch_group_notify

这里需要注意的是, dispatch_group_enter()和 dispatch_group_leave()需要一一对应,否则会发生crash。

当然除了可以异步添加到组并发队列中,也可以同步添加,还可以添加到串行队列中,这里就不一一列举了,你们可以试试。

二、dispatch_semaphore_t信号量:

1、概念:信号量基于计数器的一种多线程同步机制。在多个线程访问共有资源时候,会因为多线程的特性而引发数据出错的问题。

2、原理:先设置一个信号总量,如果信号总量的整形参数是0 ,那么就是没有资源需要等待,我们如果下面执行dispatch_semaphore_wait() 操作,那么相当于线程拥堵,执行信号-1 操作,如果现在是绿灯通行状态,我们会设置 dispatch_semaphore_signal()信号,执行一个信号+1 操作,告诉当前线程,有一个信号可以释放了。

最大并发数为3

三、多个本地文件上传简易工具:

1、这个工具就是基于上述两个API的技术,将上传的任务添加到group中,然后通过dispatch_semaphore_t信号量来控制最大并发数,默认3,外面可以设置;

2、封装了一个fileModel类,里面包含本地路径、上传的状态、远端路径、上传任务;

3、可以多次添加任务,重复添加同一个本地文件,只会上传一次,上传缓存uploadDic是一个以本地文件路径为key,file model为value的;

4、上传失败后,回将对应的状态标记为上传失败,将是否需要重试上传标记为yes,并在第一次所有的图片上传完成后出发,最多重试3次;

5、可以中途取消上传任务,通过NSURLSessionUploadTask实现。

6、当所有的任务(包括重试任务)完成后,通过dispatch_group_notify通知所有文件上传完成后,block回调NSArray<UploadFileModel*>。

工具整体架构
filemodel类

UploadFilesTool.m代码如下:

-(instancetype)init{

    if(self= [superinit]) {

        self.semo = dispatch_semaphore_create(self.maxQueueCount);

        self.queue = dispatch_queue_create("HXWUPLOADQUEUE", DISPATCH_QUEUE_CONCURRENT);

        self.group = dispatch_group_create();

        self.needRestart=NO;

        self.lock= [NSLocknew];

        self.reUploadCount=3;

    }

    return self;

}

///第一次提交待上传的本地文件路径集合

- (void)startLocalFilePaths:(NSArray*)filePaths completion:(CompletionHandler)handler{

    if(handler) {

        self.handler= handler;

    }

    for(NSString* filePathinfilePaths) {

        UploadFileModel* model = [self.uploadDicobjectForKey:filePath];

        if(!model) {

            model = [UploadFileModelnew];

            model.state=UploadStateWaiting;

            model.originFilePath= filePath;

            model.remoteUrl=@"";

            [self.uploadDicsetObject:modelforKey:filePath];

        }

        ///取消了,再次添加到上传队列,设置为待上传状态

        if([model.remoteUrlisEqualToString:@""] && model.state==UploadStateCancel) {

            [self updateMode:model state:UploadStateWaiting];

        }

    }

    [self startUpload];

}

///中途提交待上传的本地文件路径集合

- (void)addLocalFilePaths:(NSArray*)filePaths{

    [self startLocalFilePaths:filePaths completion:nil];

}

///取消上传

- (void)cancelUploadLocalFilePaths:(NSArray*)filePaths{

        for(NSString* filePathinfilePaths) {

            UploadFileModel* model = [self.uploadDicobjectForKey:filePath];

            if(model) {///置为取消状态,并取消任务

                [self updateMode:model state:UploadStateCancel];

                if(model.task) {

                    [model.taskcancel];

                }

            }

        }

}

///开始上传

- (void)startUpload{

    [self.locklock];

    if (self.needRestart) {

        self.needRestart=NO;

    }

    [self.lockunlock];

    __weaktypeof(self) weakSelf =self;

    [self.uploadDic.allValuesenumerateObjectsUsingBlock:^(UploadFileModel*  _Nonnullobj,NSUIntegeridx,BOOL*_Nonnullstop) {

        if (obj.state == UploadStateWaiting ||obj.state == UploadStateFailed) {

            obj.state=UploadStateUploading;

            ///记得enter跟leave一一对应

            dispatch_group_enter(weakSelf.group);

            dispatch_group_async(weakSelf.group, weakSelf.queue, ^{

                ///信号量减1,小于0等待

                dispatch_semaphore_wait(weakSelf.semo,DISPATCH_TIME_FOREVER);

                obj.task= [weakSelf.delegateuploadFile:obj.originFilePathsuceed:^(NSString*remoteUrl) {

                    [weakSelfupdateMode:objstate:UploadStateSucessed];

                    obj.remoteUrl= remoteUrl;

                    obj.task=nil;///任务置空

                    ///信号量加1

                    dispatch_semaphore_signal(weakSelf.semo);

                    ///记得enter跟leave一一对应

                    dispatch_group_leave(weakSelf.group);

                }failed:^(NSString*des,NSIntegercode) {

                    if(obj.state==UploadStateUploading) {

                        ///不是主动取消,设置为failed

                        [weakSelfupdateMode:objstate:UploadStateFailed];

                    }

                    obj.task=nil;///任务置空

                    ///信号量加1

                    dispatch_semaphore_signal(weakSelf.semo);

                    ///记得enter跟leave一一对应

                    dispatch_group_leave(weakSelf.group);

                }progress:^(doubleprogress) {

                    ///备用

                }];

            });

        }

    }];

    ///全部上传完成通知结果

    dispatch_group_notify(self.group, dispatch_get_main_queue(), ^{

        ///上传失败,需要重传,并且次数减1

        if(weakSelf.needRestart&& weakSelf.reUploadCount>0) {

            weakSelf.reUploadCount--;

            [weakSelfstartUpload];

        }else{

            if(weakSelf.handler) {

                weakSelf.handler(weakSelf.uploadDic.allValues);

            }

        }

    });

}

- (void)updateMode:(UploadFileModel*)mode state:(UploadState)state{

    [self.locklock];

    mode.state= state;

    if(state ==UploadStateFailed) {

        self.needRestart=YES;

    }

    [self.lockunlock];

}

-(NSMutableDictionary *)uploadDic{

    if(!_uploadDic) {

        _uploadDic = [NSMutableDictionary new];

    }

    return _uploadDic;

}

-(NSUInteger)maxQueueCount{

    if (_maxQueueCount == 0) {

        _maxQueueCount = 1;

    }

    return _maxQueueCount;

}

具体代码点这里

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容

  • 本文用来介绍 iOS 多线程中 GCD 的相关知识以及使用方法。这大概是史上最详细、清晰的关于 GCD 的详细讲...
    花花世界的孤独行者阅读 497评论 0 1
  • 很久前的总结,今天贴出来。适合看了就用,很少讲解,纯粹用法。 目录 Dispatch Queue dispatch...
    和女神经常玩阅读 633评论 0 3
  • ***********单例************************************** http:...
    树懒啊树懒阅读 601评论 0 2
  • 目前因项目需要,调用QQ音乐的接口,发现返回的数据是一个带有编码格式的歌词,所以就本着未来重复使用的方便,就封装了...
    小川载舟阅读 1,557评论 0 1
  • 假如可以,就让我装饰每天的第一缕阳光为你,清风拂面,满室芬芳。 鲁莽的阳光肆意在你的眉眼,唇边猜测你的心意。在这个...
    echo_720a阅读 188评论 0 1