如何并行跟踪两个 NSProgress?

Posted

技术标签:

【中文标题】如何并行跟踪两个 NSProgress?【英文标题】:How to track two NSProgress in parallel? 【发布时间】:2015-07-06 18:00:13 【问题描述】:

我有两个不同的大任务,每个都有几个子任务。每个子任务都有一个子 NSProgress 我手动更新。每个大任务都有一个父 NSProgress 有几个

[progress becomeCurrentWithPendingUnitCount:1.0]
// Perform subtask which generates the child `NSProgress`.
[progress resignCurrent]

在不同的时间打电话。两个大型任务进度报告中的每一个都适用于此设置。

我的问题是我想并行执行这两个大任务,并且我想整体跟踪它们的进度。有没有办法做到这一点?

我尝试在外层创建一个NSProgress 对象来包装两个大任务的NSProgress(总共三个NSProgress 层),但问题是两个任务在更新进度时“打架” .也就是说,一个任务可以执行becomeCurrentWithPendingUnitCount:,然后另一个任务可以执行resignCurrent,这会导致异常(因为第二个任务的NSProgress 不是当前的)。

使这两个任务顺序而不是并行可以解决问题,但我真的很想同时执行它们。有什么想法吗?

【问题讨论】:

【参考方案1】:

是的,您可以使用NSProgress 并行运行 2 个异步操作。这是我所做的事情的简单介绍。

   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^
        [self.progress becomeCurrentWithPendingUnitCount:40];
        [self startAsyncTask1];
        [self.progress resignCurrent];
        [self.progress becomeCurrentWithPendingUnitCount:60];
        [self startAsyncTask2];
        [self.progress resignCurrent];
    );

- (void)startAsyncTask1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^
        NSProgress *taskProgress = [NSProgress progressWithTotalUnitCount:allUsers.count];
        [taskProgress becomeCurrentWithPendingUnitCount:allUsers.count];
        [self startUploadingAllUsers];
        [taskProgress resignCurrent];
    );

-(void)startUploadingAllUsers
    for(id user in allUsers)
        [self uploadUser:user];
    

-(void)uploadUser:(id)user
    NSProgress *taskProgress = [NSProgress progressWithTotalUnitCount:user.photos.count];
    //Do upload and in completion of photo upload increment taskProgress.completedUnitCount
    //This last task does not get resigned. It does not become active either.

您必须确保您的 2 个任务是异步的。这意味着即使任务仍在执行,也会立即调用 resign 调用。当我尝试在完成异步任务时辞职时,由于NSProgress 已经在其他地方辞职,因此出现了该错误。所以就像我的示例 Task1 和 Task2 也是异步的,并且其中还包含子 NSProgresses。您或多或少希望在启动异步任务后立即退出当前进度,而不是等到它完成。

作为旁注,我喜欢使用 100 个单位作为待处理,因此我可以将每个任务视为其中的百分比。您也可以对待处理的单元使用字节数,但我倾向于在发生实际数据处理的较低子进程而不是在父进程中这样做。就像我的示例一样,我分派了一个异步任务,用于将所有新用户对象上传到 API,以及一个用于上传所有新用户照片的过程。照片任务计算照片的字节大小并将其用于更新子进程,但父任务大约占主任务的 40%,这就是我使用百分比的原因,因为有时如果你不知道聚合字节数具有复杂的多对象进程。

【讨论】:

您的任务的孩子NSProgress 是否也调用becomeCurrentWithPendingUnitCount:resignCurrent?我的问题是我的(这些聚合任务有自己的NSProgress,然后是更多的孩子NSProgress),所以任务中的becomeCurrentWithPendingUnitCount:resignCurrent 调用会被调用或排序。我认为如果您的任务仅使用叶 NSProgress(两级 NSProgress 子级而不是三级),这对您来说不是问题。 所以我的 startAsyncTask1 方法示例创建了一个本地 NSProgress 并说我有 10 个用户要同步,因此它将总单位设置为 10 并成为当前有 10 个待处理。它调用启动异步的 api 调用并在任何这些异步调用返回之前退出。在这些 API 调用中,我为每个调用创建一个NSProgess,但最低级别的进度不应该重新签名。所以在 API 调用中,我从不辞职,我只更新已完成的单元。 我用更多代码更新了答案。您将看到最终的子任务没有激活,也没有退出。我的真实世界代码是上传所有本地对象、上传所有本地创建的照片并从服务器下载所有新对象的任务。因此,我为每个对象类型创建了一个子进度,例如 1 用于用户任务,1 用于照片任务,一直到单个 API 调用。我的深度是NSProgress 的 3 层。 啊,我知道你是怎么做到的。我的问题是这个大任务有一些需要按顺序执行的异步子任务(因此,后期子任务的NSProgress 只在以后创建)。这就是导致我对两个大任务“战斗”的问题的原因。在您的情况下,您似乎同时开始所有上传(因此,您的所有 NSProgress 层次结构都是在复合操作开始时创建的)。 另外,我认为您的示例中有一个不必要的dispatch_async 电话。 startAsyncTask1 它已经被后台队列调用。

以上是关于如何并行跟踪两个 NSProgress?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在 AFNetworking 中使用点对点类型(NSProgress * __autoreleasing *)而不仅仅是点类型(NSProgress * __autoreleasing)?

如何在异步函数中并行化 for 循环并跟踪 for 循环执行状态?

iOS - 无法调用非函数类型“NSProgress”的值

NSProgress::completedUnitCount 设置器挂起

使用强大的 NSProgress 和 downloadtaskwithrequest

进度开始后是不是可以动态扩展 NSProgress 层次结构?