iOS-管理和跟踪多个并发任务

时间:2013-08-15 20:35:24

标签: ios objective-c multithreading cocoa-touch grand-central-dispatch

在我的应用中,我需要从多个来源加载数据并将它们放在表格视图中。一个接一个地收集每个来源将永远需要。为了解决这个问题,我需要一起运行所有下载操作。由于它们是下载任务,理论上我可以运行它们,但问题是线程上只有部分代码是异步运行的,这意味着它需要主线程才能完成操作。

所以为了让所有这些都在后台运行,我需要使用GCD,我没有多少经验。

//DataLoader.m

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
    [self.webLoader getFeedWithCompletion:self.thatOtherCompletionBlock];
    [self.otherDataLoader getDataWithCompletion:self.completionBlock];
    [self.thatDataLoader getThatDataWithCompletion:self.anotherCompletionBlock]
    dispatch_async(dispatch_get_main_queue(), ^(void){

    });
});

但是,由于部分任务已经异步,我需要弄清楚GCD代码的放置位置。

我可以在开始任务之前把它放进去,就像我上面做的那样。但是,这可能有效,因为任务已经在后台部分运行(在某些情况下我无法改变),运行已在后台部分在后台运行的任务似乎很浪费。为什么运行已在另一个线程中的后台线程中运行的东西?

另一种选择是在获取Feed的实际类中使用GCD(例如webloader),将其放在所有未在后台运行的代码上

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
    .......

 });

哪种方式更好?

还有另一个问题。由于部分任务是异步的,因此它们使用完成块。我不仅需要在后台运行完成块,我还需要确定哪一个是最后一个完成,所以我可以运行一些代码来清理并整齐地打包并将数据发送到视图控制器。

我想到的方法是为每个任务使用BOOL,只需在完成后将其更改为true。然后在我的完成块中,我可以检查所有其他任务是否完成,如果是,则运行清理代码。但是,这可能不是最优雅的解决方案。

处理这些任务的最佳方法是什么,确保这一切都在后台发生?

1 个答案:

答案 0 :(得分:5)

GCD组可以很容易地用于此。组允许您跟踪组中的任意“成员”,并挂起块以在组的所有成员完成时运行。这很方便。例如(使用您的代码):

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
    dispatch_group_t group = dispatch_group_create();

    dispatch_group_enter(group); // + 1
    [self.webLoader getFeedWithCompletion: ^{
        self.thatOtherCompletionBlock();
        dispatch_group_leave(group); // - 1
    }];

    dispatch_group_enter(group); // + 1
    [self.otherDataLoader getDataWithCompletion:^{
        self.completionBlock();
        dispatch_group_leave(group); // - 1
    }];

    dispatch_group_enter(group); // + 1
    [self.thatDataLoader getThatDataWithCompletion:^{
        self.anotherCompletionBlock();
        dispatch_group_leave(group); // - 1
    }];

    dispatch_group_notify(group, dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // This will get executed once all three of the prior completion blocks have been run.
        // i.e. when the group "count" goes to zero.
    });

    dispatch_release(group);
});

尽管有点迂回,您也可以使用NSOperation的操作间依赖性功能来实现这一目标。像这样:

NSOperationQueue* q = [[[NSOperationQueue alloc] init] autorelease];

NSOperation* completionA = [NSBlockOperation blockOperationWithBlock: self.thatOtherCompletionBlock];
NSOperation* completionB = [NSBlockOperation blockOperationWithBlock: self.completionBlock];
NSOperation* completionC = [NSBlockOperation blockOperationWithBlock: self.anotherCompletionBlock];

NSBlockOperation* afterAllThree = [[[NSBlockOperation alloc] init] autorelease];
[afterAllThree addDependency: completionA];
[afterAllThree addDependency: completionB];
[afterAllThree addDependency: completionC];
[afterAllThree addExecutionBlock:^{
    // This will get executed once all three of the prior completion blocks have been run.
}];

// Kick off the tasks
[q addOperationWithBlock:^{
    [self.webLoader getFeedWithCompletion: ^{ [q addOperation: completionA];}];
    [self.otherDataLoader getDataWithCompletion:^{ [q addOperation: completionB]; }];
    [self.thatDataLoader getThatDataWithCompletion:^{ [q addOperation: completionC]; }];
}];

我个人更喜欢dispatch_group方法,但他们都会完成工作。