是否可以运行多个后台线程来提高 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/