如何在没有达到全球GCD限制的情况下并行执行许多(100+)任务?

问题是

当在后台中延迟加载100个图标列表时,我点击了GCD线程限制(64个线程),这导致我的应用程序在主线程上通过semaphore_wait_trap冻结。 我想重构我的线程代码,以防止这种情况发生,同时仍然加载图标asynchronous,以防止UI阻塞。

背景

我的应用程序加载了一个SVG图标的屏幕。 金额平均从10-200不等。 通过使用本地SVG图像或远程SVG图像(如果它具有自定义图标)绘制图标,然后对它们进行后处理以获得最终的图像结果。

因为这需要一些时间,而且对用户来说不是至关重要的,所以我想在后台加载和后处理它们,所以它们会随着时间的推移而popup。 对于每个图标我使用以下内容:

 dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(concurrentQueue, ^{ //code to be executed in the background SVGKImage *iconImage = [Settings getIconImage:location]; dispatch_async(dispatch_get_main_queue(), ^{ //code to be executed on the main thread when background task is finished if (iconImage) { [iconImgView setImage:iconImage.UIImage]; } }); }); 

getIconImage方法处理基本SVG的初始加载,如果它是本地的,它将与[NSInputStream inputStreamWithFileAtPath:path]同步读取,如果它应该远程加载, [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&errorWithNSData] 。 这一切都是同步的。

然后,在返回并放入主线程的UIImageView之前,有一些重新着色SVG的后处理。

题:

有没有一种方法来构build我的代码,以允许并行后台加载,但防止由于太多的线程死锁?

解决scheme编辑:

 _iconOperationQueue = [[NSOperationQueue alloc]init]; _iconOperationQueue.maxConcurrentOperationCount = 8; // Code will be executed on the background [_iconOperationQueue addOperationWithBlock:^{ // I/O code SVGKImage *baseIcon = [Settings getIconBaseSVG:location]; // CPU-only code SVGKImage *iconImage = [Settings getIconImage:location withBaseSVG:baseIcon]; UIImage *svgImage = iconImage.UIImage; // Converting SVGKImage to UIImage is expensive, so don't do this on the main thread [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // Code to be executed on the main thread when background task is finished if (svgImage) { [iconImgView setImage:svgImage]; } }]; }]; 

不要直接使用带有并发队列的GCD,而要使用NSOperationQueue 。 将其maxConcurrentOperationCount设置为合理的值,如4或8。

如果可以的话,还应该将I / O从纯计算中分离出来。 对I / O使用宽度受限的操作队列。 纯计算可以使用不受限制的操作队列或纯GCD。

原因是I / O块。 GCD检测到系统处于空闲状态,并激活另一个工作线程,并从队列中启动另一个任务。 这也阻止了I / O,所以它会做更多的工作,直到达到极限。 然后,I / O开始完成,任务解除阻塞。 现在你已经超额认购了系统资源(比如CPU),因为有更多的任务在运行,而不是核心,突然间他们实际上是在使用CPU而不是被I / O阻塞。

纯粹的计算任务不会引发这个问题,因为GCD认为系统实际上是繁忙的,并且在较早的系统完成之前不会出列更多的任务。

你可以通过使用像这样的信号量来保持GCD,在后台运行整个操作,否则等待信号量将会停止UI:

 dispatch_semaphore_t throttleSemaphore = dispatch_semaphore_create(8); dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); for /* Loop through your images */ { dispatch_semaphore_wait(throttleSemaphore, DISPATCH_TIME_FOREVER); dispatch_async(concurrentQueue, ^{ //code to be executed in the background SVGKImage *iconImage = [Settings getIconImage:location]; dispatch_async(dispatch_get_main_queue(), ^{ //code to be executed on the main thread when background task is finished if (iconImage) { [iconImgView setImage:iconImage.UIImage]; } dispatch_semaphore_signal(throttleSemaphore); }); }); }