简体   繁体   中英

How to track two NSProgress in parallel?

I have two different big tasks with several subtasks each. Each subtask has a child NSProgress which I update manually. Each big task hosts a parent NSProgress with several

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

calls at different times. Each of the two big task progress reporting works fine with this setup.

My problem is that I want to execute these two big tasks in parallel, and I want to track their progress as a whole. Is there any way to do this?

I have tried creating a NSProgress object at the outer level to wrap the two big task's NSProgress (so three NSProgress levels in total), but the problem is that the two tasks "fight" when updating progress. That is, one task could execute becomeCurrentWithPendingUnitCount: and then the other task could execute resignCurrent which causes an exception (since the second task's NSProgress is not current).

Making the two tasks sequential instead of parallel fixes the issue, but I really would like to execute them at the same time. Any ideas?

Yes you can run 2 async operations in parallel with an NSProgress . Here's a snippet of what I do.

   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.
}

You have to make sure your 2 tasks are asynchronous. That means the resign call is called right away, even though the task is still executing. When I tried to resign in the completion of an async task, I got that error due to the fact that the NSProgress had already been resigned elsewhere. So Like my example Task1 and Task2 are also async and also contain child NSProgresses in them. You more or less want to resign the current progress right after kicking off the async task and not waiting until it is done.

As a side note I like to use 100 units as the pending so I can think of each task as a percentage of it. You could also use byte count for units pending but I tend to do that at the lower child processes where actual data processing happens and not in the parent process. Like my example I dispatch an async task for uploading all new User objects to an API and a process for uploading all new user photos. The photo task counts the byte size of the photo and uses that for updating the child process but the parent task is about 40% of the main task and that's why I use percentages because some times you don't know the aggregated byte count if you have complex multiple object processes.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM