运行多个后台线程iOS

时间:2016-02-01 15:38:45

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

是否可以运行多个后台线程来提高iOS的性能。目前我正在使用以下代码发送,例如在后台线程上发送50个网络请求:

 dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
            // send 50 network requests 
 });

修改

将我的代码更新为类似的内容后,没有取得任何性能提升:(取自here

dispatch_queue_t fetchQ = dispatch_queue_create("Multiple Async Downloader", NULL);
dispatch_group_t fetchGroup = dispatch_group_create();

// This will allow up to 8 parallel downloads.
dispatch_semaphore_t downloadSema = dispatch_semaphore_create(8);

// We start ALL our downloads in parallel throttled by the above semaphore.
for (NSURL *url in urlsArray) {
    dispatch_group_async(fetchGroup, fetchQ, ^(void) {
        dispatch_semaphore_wait(downloadSema, DISPATCH_TIME_FOREVER);
        NSMutableURLRequest *headRequest = [NSMutableURLRequest requestWithURL:url  cachePolicy: NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
        [headRequest setHTTPMethod: @"GET"];
        [headRequest addValue: cookieString forHTTPHeaderField: @"Cookie"];

         NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];
        [NSURLConnection sendAsynchronousRequest:headRequest
                                           queue:queue // created at class init
                               completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
                                   // do something with data or handle error
                                   NSLog(@"request completed");
                               }];
        dispatch_semaphore_signal(downloadSema);
    });
}

// Now we wait until ALL our dispatch_group_async are finished.
dispatch_group_wait(fetchGroup, DISPATCH_TIME_FOREVER);

// Update your UI
dispatch_sync(dispatch_get_main_queue(), ^{
    //[self updateUIFunction];
});

// Release resources
dispatch_release(fetchGroup);
dispatch_release(downloadSema);
dispatch_release(fetchQ);

2 个答案:

答案 0 :(得分:4)

注意不要将线程与队列混淆

单个并发队列可以跨多个线程运行,GCD从不保证您的任务将在哪个线程上运行。

您目前拥有的代码将提交50个网络任务以在后台并发队列上运行,这是真的。

但是,所有这50项任务都将在相同的主题上执行。

GCD基本上就像一个巨大的线程池,所以你的块(包含你的50个任务)将被提交到池中的下一个可用线程。因此,如果任务是同步的,它们将以串行执行。这意味着每个任务必须等到前一个任务完成才能完成。如果它们是异步任务,那么它们将立即被调度(这就是为什么首先需要使用GCD的问题)。

如果您希望同时运行多个同步任务,则需要为每个任务单独dispatch_async。这样,您就可以使用块 per 任务,因此它们将被分派到线程池中的多个线程,因此可以同时运行。

虽然您应该小心不要提交太多网络任务来同时运行(您没有具体说明他们正在做什么),因为它可能会使服务器,as gnasher says

您可以使用GCD 信号量轻松限制同时运行的并发任务(无论是同步还是异步)的数量。例如,此代码将并发操作的数量限制为6:

long numberOfConcurrentTasks = 6;

dispatch_semaphore_t semaphore = dispatch_semaphore_create(numberOfConcurrentTasks);

for (int i = 0; i < 50; i++) {

    dispatch_async(concurrentQueue, ^{

        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

        [self doNetworkTaskWithCompletion:^{
            dispatch_semaphore_signal(semaphore);
            NSLog(@"network task %i done", i);
        }];

    });

}

修改

您的代码存在问题:

dispatch_queue_t fetchQ = dispatch_queue_create("Multiple Async Downloader", NULL);

NULL传递给attr参数时,GCD会创建一个串行队列(如果您在此处实际指定了队列类型,它也会更具可读性)。你想要一个并发队列。因此,你想要:

dispatch_queue_t fetchQ = dispatch_queue_create("Multiple Async Downloader", DISPATCH_QUEUE_CONCURRENT);

您需要在请求结尾处的请求的完成处理程序中发信号通知您的信号量。由于它是异步的,因此一旦请求被发送,信号量就会发出信号,因此排队另一个网络任务。您希望在发出信号之前等待网络任务返回。

[NSURLConnection sendAsynchronousRequest:headRequest
                                       queue:queue // created at class init
                           completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
                           // do something with data or handle error
                           NSLog(@"request completed");
                           dispatch_semaphore_signal(downloadSema);
                       }];

编辑2

我刚刚注意到您正在使用dispatch_sync更新您的用户界面。我认为它没有理由是同步的,因为它只会阻塞后台线程,直到主线程更新了UI。我会用dispatch_async来做这件事。

编辑3

作为CouchDeveloper points out,系统可能会限制并发网络请求的数量。

最简单的解决方案似乎是迁移到NSURLSession 并配置所使用的maxConcurrentOperationCount的{​​{1}}属性。这样你就可以完全抛弃信号量,只需在后台队列上调度所有网络请求,使用回调来更新主线程上的UI。

我对NSOperationQueue并不熟悉,但我只是从GCD的角度回答这个问题。

答案 1 :(得分:1)

您可以发送多个请求,但并行发送50个请求通常一个好主意。面对50个同时请求的服务器很可能会处理前几个请求并返回其余的错误。这取决于服务器,但使用信号量,您可以轻松地将运行请求的数量限制为您喜欢的任何内容,比如四或八。您需要尝试使用有问题的服务器来找出在该服务器上可靠运行的内容并为您提供最高性能。

并且似乎有一些混乱:通常所有的网络请求都将异步运行。那就是你把请求发送到操作系统(通常非常快),然后暂时没有任何反应,然后调用你的回调方法,处理数据。无论是从主线程还是从后台线程发送请求都没有太大区别。

处理这些请求的结果可能非常耗时。您可以在后台线程上处理结果。您可以在同一个串行队列上处理所有请求的结果,这样可以更轻松地避免多线程问题。这就是我的工作,因为它很容易,即使在最坏的情况下使用一个处理器进行密集处理结果,而另一个处理器可以做UI等。

如果您使用同步网络请求(这是一个坏主意),那么您需要在后台线程上自行调度每个请求。如果在后台线程上运行50个同步网络请求的循环,则第二个请求将等到第一个请求完成。