首页 > 其他分享 >iOS开发基础133-GCD相关

iOS开发基础133-GCD相关

时间:2024-07-18 19:10:34浏览次数:14  
标签:信号量 group GCD iOS dispatch 133 uploadResults semaphore 上传

先看一段代码,这是项目中图片上传的一部分代码。

// 开启线程组上传图片
    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];
    });

问题

好的,现在我们要求必须使用并行队列并进行异步上传的条件下,不能借助上面代码中出现的idx,如何做到确保上传顺序不变?

解决

使用并行队列并进行异步上传的条件下,我们可以通过在并行队列中使用信号量及同步阻塞的方式来确保上传顺序,同时在完成所有上传任务后回到主线程进行后续操作。

这里有个关键点是我们要确保上传的结果按顺序存储,同时又要利用并行队列进行异步操作。

使用并行队列、信号量和计数器

以下是一个实现上传功能的例子,该例子在并行队列中异步进行图片上传,并使用信号量来控制并发数,确保上传结果的顺序性。

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];
});

说明:

  1. 创建一个并行队列 uploadQueue 和一个 dispatch_group 来跟踪所有上传任务。
  2. 创建一个信号量 semaphore 来控制最大并发数。
  3. 初始化 uploadResults 数组,并使用 NSNull 进行占位。
  4. 使用一个 for 循环对每张图片进行操作:
    • 每次进入 dispatch_group,并等待信号量(确保并发量)。
    • 在并行队列中异步执行上传任务。
    • 上传完成后更新 uploadResults 数组,同时释放信号量和 dispatch_group
  5. 当所有上传任务完成后,会调用 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);
        }];
    });
}
  • 这里有几个步骤:
    1. 使用 dispatch_group_enter(group) 将当前循环的上传任务加入到 dispatch_group 中。
    2. 使用 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) 等待信号量,确保不会超过最大并发数。
    3. 通过 dispatch_async(uploadQueue, ^{...}) 异步在并行队列中执行上传任务。
    4. 在上传任务的回调中,通过 @synchronized (uploadResults) 确保对 uploadResults 数组 的插入操作是线程安全的。
    5. 根据上传结果更新 uploadResults,使用传入的 i 确保结果按顺序存储。
    6. 使用 dispatch_semaphore_signal(semaphore) 释放信号量,允许下一个任务进入。
    7. 通过 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) 调用完成后执行。

如何保证顺序

  1. uploadResults 数组:

    • 初始化时,用 NSNull 进行占位,确保数组的大小和上传的图片数量一致。
    • 通过图片在 self.selectedPhotos 中的索引 i 进行结果存储。即 uploadResults[i],保证了上传结果的顺序。
  2. 信号量 semaphoredispatch_group:

    • 信号量控制了并发的最大数量,每次上传任务开始时会等待信号量减1,结束时信号量加1,这样确保了我们不会一次性开始过多的任务。
    • dispatch_group 用于跟踪这组任务的完成状态,在所有任务完成后通过 dispatch_group_notify 通知主线程。
  3. 同步机制 @synchronized:

    • 确保在多线程环境下对 uploadResults 数组的修改是线程安全的,防止多个线程同时访问修改同一个数组对象。

通过以上设计,我们能够在并行队列中异步上传图片,同时确保每个任务按照初始顺序将结果存入 uploadResults 数组,最终在所有任务完成后回到主线程进行后续操作。

标签:信号量,group,GCD,iOS,dispatch,133,uploadResults,semaphore,上传
From: https://www.cnblogs.com/chglog/p/18310258

相关文章

  • iOS开发基础133-崩溃预防
    现代移动应用的用户体验依赖于其稳定性和可靠性。然而,在开发过程中,我们时常会遇到各种崩溃问题。崩溃不仅会影响用户的使用体验,还可能损害应用的声誉。因此,本文将详细介绍一个名为CrashPrevention的工具类,它能够为iOS开发者提供多方面的崩溃预防措施,借助该工具类,开发者能够有效减......
  • iOS开发基础132-POSIX线程库
    POSIX线程库,通常称为Pthreads(POSIXThreads),是一个基于POSIX标准的多线程编程接口。它为多线程应用程序提供了一组标准化的API,兼容多个UNIX系统,包括Linux、macOS等。POSIX线程库概览POSIX线程库主要包括以下几个组成部分:线程管理:创建和操作线程。线程同步:互斥锁(mut......
  • iOS开发基础131-isa指针
    iOS中isa指针是Objective-C对象内部的一个重要概念,它是实现对象与类之间关系的核心机制。深入理解isa指针对掌握Objective-C的底层运行机制和对象模型非常重要。1.什么是isa指针每个Objective-C对象都有一个isa指针,它指向这个对象所属的类。类本身也有一个isa指针,指向其元类(met......
  • iOS开发基础129-音频录制上传
    在Objective-C中,音频录制过程涉及几个关键步骤,包括配置录音设置、创建和启动录音机、处理录音会话以及将录制的音频文件上传到服务器。下面是一个详细的示例,包括创建一个简单的音频录制应用,以及将录制的音频文件上传到服务器的代码。1.设置音频会话我们需要使用AVFoundation框......
  • 扩展欧几里得算法(exGcd)
    扩展欧几里得算法(ExtendedEuclideanalgorithm,EXGCD),常用于求\(ax+by=c\)的一组可行解。过程设\(ax_1+by_1=\gcd(a,b)\)\(bx_2+(a\modb)y_2=gcd(b,a\modb)\)由欧几里得算法:\(\gcd(a,b)=gcd(b,a\modb)\)所以:\(ax_1+by_1=bx_2+(a\modb)y_2\)又因为:\(a\mod......
  • iOS开发基础127-深入探讨KVO
    一、基础KVO(Key-ValueObserving,键值观察)是Cocoa提供的一种机制,它允许我们观察属性的变化并做出响应。这种机制非常强大,广泛应用于各种编程场景,如数据绑定、状态变化监控等。在深入了解KVO之前,我们先从KVO的基本概念开始,然后逐步探讨其深层次应用和一些使用实践的注意事项......
  • 在 PowerShell 中Get-WmiObject Win32_PhysicalMemory,SMBIOSMemoryType 是一种用于描
    在PowerShell中Get-WmiObjectWin32_PhysicalMemory,SMBIOSMemoryType是一种用于描述系统中物理内存类型的属性。数字26表示特定的内存类型,具体为DDR4内存。每种内存类型在SMBIOS(SystemManagementBIOS)规范中都有一个对应的数字码,用来标识不同类型的内存。以下是一些常见......
  • iOS开发基础125-深入探索SDWebImage
    SDWebImage是一个流行的用于处理图像下载和缓存的库,广泛用于iOS开发中,提供了一系列方便的API来下载和缓存图像,以提高应用的性能和用户体验。以下是对其进行详细介绍和分析,包括其原理和底层实现。一、SDWebImage的主要功能图像下载和缓存:图像下载:使用异步方式从网络上下......
  • iOS开发基础124-RunLoop实现卡顿检测
    利用RunLoop实现卡顿检测的基本思路是通过监听RunLoop的状态变化来判断主线程的执行时长。如果RunLoop在某个状态停留的时间超过了预设的时间阈值,则认为发生了卡顿。在具体实现中,可以利用CFRunLoopObserver来监听RunLoop的状态变化,并记录时间差。一、卡顿检测的基本原......
  • iOS开发基础122-RunLoop
    深入探讨RunLoop的底层实现需要了解CoreFoundation框架中的CFRunLoop以及与RunLoop工作机制紧密相关的操作系统底层API。这些底层实现主要涉及到事件源、定时器和线程的调度机制。本文将深入剖析RunLoop的底层结构及其运行流程。一、RunLoop底层数据结构涉及RunLo......