iOS开发基础134-异步并行上传问题
1.iOS开发基础77-一像素线的几种实现方式2.iOS开发基础78-iOS 国际化3.iOS开发基础82-关于iOS目录4.iOS开发基础81-Runtime实战5.iOS开发基础80-关于Xcode86.iOS开发基础79-强制退出程序7.iOS开发基础90-密码学8.iOS开发基础89-Runloop9.iOS开发基础88-涂鸦效果10.iOS开发基础87-抽屉效果11.iOS开发基础86-FMDB12.iOS开发基础85-线程dispatch apply13.iOS开发基础84-HTTP请求方法详解与增删改查的应用14.iOS开发基础83-线程组15.iOS开发基础106-Instruments16.iOS开发基础105-Xcode收集Crashs的各种方法17.iOS开发基础104-正向代理和反向代理18.iOS开发基础103-APP之间跳转19.iOS开发基础102-后台保活方案20.iOS开发基础101-指纹和面部识别21.iOS开发基础100-MDM证书申请流程22.iOS开发基础99-iOS 内购的防范与优化23.iOS开发基础98-跳转淘宝案例24.iOS开发基础97-应用内购(In-App Purchase)的安全性解析与收据处理流程25.iOS开发基础96-UI类继承关系图26.iOS开发基础95-程序内评价27.iOS开发基础94-xcode1028.iOS开发基础93-GCD死锁29.iOS开发基础92-线程保活30.iOS开发基础91-线程同步技术与资源共享详解31.iOS开发基础138-视频编码32.iOS开发基础137-音视频编解码简介33.iOS开发基础136-防暴力点击34.iOS开发基础135-Core Data
35.iOS开发基础134-异步并行上传问题
36.iOS开发基础133-崩溃预防37.iOS开发基础132-POSIX线程库38.iOS开发基础131-isa指针39.iOS开发基础130-视频录制上传40.iOS开发基础129-音频录制上传41.iOS开发基础128-应用本地化42.iOS开发基础127-深入探讨KVO43.iOS开发基础126-深入探索设计模式44.iOS开发基础125-深入探索SDWebImage45.iOS开发基础124-RunLoop实现卡顿检测46.iOS开发基础123-自动释放池原理47.iOS开发基础122-RunLoop48.iOS开发基础121-APP启动优化49.iOS开发基础120-通知与线程50.iOS开发基础119-组件化51.iOS开发基础118-Runtime52.iOS开发基础117-Hybrid53.iOS开发基础116-性能监控54.iOS开发基础115-Socket55.iOS开发基础114-YYCache56.iOS开发基础113-Unity3D57.iOS开发基础112-GCD常见场景58.iOS开发基础111-RAC59.iOS开发基础110-Core Graphics应用场景60.iOS开发基础109-网络安全61.iOS开发基础108-常见的编程范式62.iOS开发基础107-iOS直播63.iOS开发基础148-ABM vs MDM64.iOS开发基础147-ABM集中管理Apple设备65.iOS开发基础146-深入解析WKWebView66.iOS开发基础145-Apple Search Ads67.iOS开发基础144-逐字打印效果68.iOS开发基础143-性能优化69.iOS开发基础142-广告归因70.iOS开发基础141-音频解码71.iOS开发基础140-音频编码72.iOS开发基础139-视频解码73.iOS开发基础149-由UUIDString引发的思考先看一段代码,这是项目中图片上传的一部分代码。
// 开启线程组上传图片
dispatch_group_t group = dispatch_group_create();
[self.selectedPhotos enumerateObjectsUsingBlock:^(UIImage * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
dispatch_group_enter(group);
[CHG_FileUpLoadNetwork uploadPicTestWithImage:obj response:^(ApiRequestStatusCode requestStatusCode, id _Nonnull JSON) {
if (requestStatusCode == CHGRequestOK) {
imageUrlDic[[NSString stringWithFormat:@"%lu",(unsigned long)idx]] = JSON[@"data"][@"cdnUrl"];
dispatch_group_leave(group);
}else{
[MBHudManager hide];
}
}];
}];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSArray *urlArray = [imageUrlDic allValues];
weakSelf.photo_paths = urlArray;
[weakSelf.feedbackAddCommand execute:nil];
});
问题
好的,现在我们要求必须使用并行队列并进行异步上传,保证线程安全的同时,是否还有其他方式确保上传顺序不变?
解决
使用并行队列并进行异步上传的条件下,我们可以通过在并行队列中使用信号量及同步阻塞的方式来确保上传顺序,同时在完成所有上传任务后回到主线程进行后续操作。
这里有个关键点是我们要确保上传的结果按顺序存储,同时又要利用并行队列进行异步操作。
使用并行队列、信号量和计数器
以下是一个实现上传功能的例子,该例子在并行队列中异步进行图片上传,并使用信号量来控制并发数,确保上传结果的顺序性。
dispatch_queue_t uploadQueue = dispatch_queue_create("com.yourapp.uploadQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_semaphore_t semaphore = dispatch_semaphore_create(5); // 设置并发的最大数量
NSMutableArray *uploadResults = [NSMutableArray arrayWithCapacity:self.selectedPhotos.count];
for (NSInteger i = 0; i < self.selectedPhotos.count; i++) {
[uploadResults addObject:[NSNull null]]; // 占位
}
for (NSUInteger i = 0; i < self.selectedPhotos.count; i++) {
dispatch_group_enter(group);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
UIImage *image = self.selectedPhotos[i];
dispatch_async(uploadQueue, ^{
[CHG_FileUpLoadNetwork uploadPicTestWithImage:image response:^(ApiRequestStatusCode requestStatusCode, id _Nonnull JSON) {
@synchronized (uploadResults) {
if (requestStatusCode == CHGRequestOK) {
uploadResults[i] = JSON[@"data"][@"cdnUrl"];
} else {
NSLog(@"Image upload failed at index: %lu", (unsigned long)i);
// 处理错误情况,如果需要,你可以在这里进行其他处理
}
}
dispatch_semaphore_signal(semaphore);
dispatch_group_leave(group);
}];
});
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
weakSelf.photo_paths = [uploadResults copy];
[weakSelf.feedbackAddCommand execute:nil];
});
说明:
- 创建一个并行队列
uploadQueue
和一个dispatch_group
来跟踪所有上传任务。 - 创建一个信号量
semaphore
来控制最大并发数。 - 初始化
uploadResults
数组,并使用NSNull
进行占位。 - 使用一个 for 循环对每张图片进行操作:
- 每次进入
dispatch_group
,并等待信号量(确保并发量)。 - 在并行队列中异步执行上传任务。
- 上传完成后更新
uploadResults
数组,同时释放信号量和dispatch_group
。
- 每次进入
- 当所有上传任务完成后,会调用
dispatch_group_notify
,在主线程中进行后续处理。
通过这种方式,我们可以在并行队列中异步上传图片,同时确保上传结果按顺序存储,并在所有上传任务完成后进行后续处理。
分析
代码详解
dispatch_queue_t uploadQueue = dispatch_queue_create("com.yourapp.uploadQueue", DISPATCH_QUEUE_CONCURRENT);
- 创建一个并行队列
uploadQueue
。在这个队列中,任务可以并行执行。
dispatch_group_t group = dispatch_group_create();
- 创建一个
dispatch_group
,用于跟踪一组任务的完成状态。我们会将所有上传任务添加到这个组中,以便监控它们的完成情况。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(5); // 设置并发的最大数量
- 创建一个信号量
semaphore
,并设置初始值为5。这用于控制并发的最大数量,防止过多的任务同时运行导致系统资源过度消耗。
NSMutableArray *uploadResults = [NSMutableArray arrayWithCapacity:self.selectedPhotos.count];
for (NSInteger i = 0; i < self.selectedPhotos.count; i++) {
[uploadResults addObject:[NSNull null]]; // 占位
}
- 初始化
uploadResults
数组,用于存储每张图片上传后的结果。数组的容量设置为选中的照片数,并使用NSNull
进行占位。这确保了后续上传结果可以按索引顺序插入。
for (NSUInteger i = 0; i < self.selectedPhotos.count; i++) {
dispatch_group_enter(group);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
UIImage *image = self.selectedPhotos[i];
dispatch_async(uploadQueue, ^{
[CHG_FileUpLoadNetwork uploadPicTestWithImage:image response:^(ApiRequestStatusCode requestStatusCode, id _Nonnull JSON) {
@synchronized (uploadResults) {
if (requestStatusCode == CHGRequestOK) {
uploadResults[i] = JSON[@"data"][@"cdnUrl"];
} else {
NSLog(@"Image upload failed at index: %lu", (unsigned long)i);
// 处理错误情况,如果需要,你可以在这里进行其他处理
}
}
dispatch_semaphore_signal(semaphore);
dispatch_group_leave(group);
}];
});
}
- 这里有几个步骤:
- 使用
dispatch_group_enter(group)
将当前循环的上传任务加入到dispatch_group
中。 - 使用
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
等待信号量,确保不会超过最大并发数。 - 通过
dispatch_async(uploadQueue, ^{...})
异步在并行队列中执行上传任务。 - 在上传任务的回调中,通过
@synchronized (uploadResults)
确保对uploadResults 数组
的插入操作是线程安全的。 - 根据上传结果更新
uploadResults
,使用传入的i
确保结果按顺序存储。 - 使用
dispatch_semaphore_signal(semaphore)
释放信号量,允许下一个任务进入。 - 通过
dispatch_group_leave(group)
标记该任务完成。
- 使用
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
weakSelf.photo_paths = [uploadResults copy];
[weakSelf.feedbackAddCommand execute:nil];
});
- 使用
dispatch_group_notify
在所有上传任务完成后回到主线程执行后续操作。这部分代码会在所有dispatch_group_leave(group)
调用完成后执行。
如何保证顺序
-
uploadResults
数组:- 初始化时,用
NSNull
进行占位,确保数组的大小和上传的图片数量一致。 - 通过图片在
self.selectedPhotos
中的索引i
进行结果存储。即uploadResults[i]
,保证了上传结果的顺序。
- 初始化时,用
-
信号量
semaphore
和dispatch_group
:- 信号量控制了并发的最大数量,每次上传任务开始时会等待信号量减1,结束时信号量加1,这样确保了我们不会一次性开始过多的任务。
dispatch_group
用于跟踪这组任务的完成状态,在所有任务完成后通过dispatch_group_notify
通知主线程。
-
同步机制
@synchronized
:- 确保在多线程环境下对
uploadResults
数组的修改是线程安全的,防止多个线程同时访问修改同一个数组对象。
- 确保在多线程环境下对
通过以上设计,我们能够在并行队列中异步上传图片,同时确保每个任务按照初始顺序将结果存入 uploadResults
数组,最终在所有任务完成后回到主线程进行后续操作。
将来的你会感谢今天如此努力的你!
版权声明:本文为博主原创文章,未经博主允许不得转载。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!