运行多个后台线程iOS

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

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

编辑:

将我的代码更新为类似的内容后,没有实现性能提升:(从这里获取

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

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

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

您可以使用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指出的那样 ,系统可能会限制并发网络请求的数量。

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

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

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

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

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

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