ios - 运行多个后台线程 iOS

标签 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);

最佳答案

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

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

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

但是,所有 50 个任务都将在同一个线程上执行。

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

如果你想要多个 同步任务同时运行,则需要单独的 dispatch_async为您的每项任务。这样,每个任务都有一个 block ,因此它们将从线程池中分派(dispatch)给多个线程,因此可以同时运行。

虽然要注意不要同时提交太多网络任务来操作 (你没有具体说明他们在做什么)因为它可能会使服务器过载,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 .我认为它没有理由同步,因为它只会阻塞后台线程,直到主线程更新 UI。我会使用 dispatch_async去做这个。

编辑 3

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

最简单的解决方案似乎是迁移到 NSURLSession 并配置maxConcurrentOperationCount NSOperationQueue 的属性(property)用过的。这样,您可以完全放弃信号量,只需将所有网络请求发送到后台队列,使用回调来更新主线程上的 UI。

我完全不熟悉NSURLSession不过,我只是从 GCD 的角度来回答这个问题。

关于ios - 运行多个后台线程 iOS,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35134427/

相关文章:

ios - AFNetworking 识别失败原因 : User cancel request or network failure

ios - 来自 iOS "Do not add subviews directly to the visual effect view itself"的警告

iphone - 如何获取CCSprite中特定像素的RGBA颜色

objective-c - NSMatrix 委托(delegate)

ios - 如何将 UIButton 设置为选中和禁用

ios - 从另一个 UIViewController 和他的事件复制 UIView

iOS 更改呈现 View Controller 上的按钮文本

java - 如何停止线程 - Java

multithreading - 如何优雅地停止长时间执行的线程?

c++ - std::atomic 与用于线程同步的静态变量