如何并行,没有击中全球GCD限制多(100+)的任务? [英] How to parallelize many (100+) tasks without hitting global GCD limit?

查看:242
本文介绍了如何并行,没有击中全球GCD限制多(100+)的任务?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题

在延迟加载在后台100+图标的列表,我打的GCD线程限制(64个线程),这将导致我的应用程序使用 semaphore_wait_trap 在主线程。我想重组这种情况的发生我的线程code至prevent,同时还装载异步prevent UI阻塞的图标。

上下文

我的应用程序加载与它SVG图标的屏幕。从10-200量平均不同。图标获得通过使用本地SVG图像或远程SVG图像(如果它有一个自定义图标)绘制,那么他们得到的后处理,以获得最终的图像效果。

由于这需要一些时间,并且它们不是至关重要对于用户来说,我要加载和后处理他们的背景,所以它们将随着时间的推移嵌入。对于每一个图标,我用的是以下内容:

  dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_async(concurrentQueue,^ {
    // code在后台执行
    SVGKImage * iconImage = [设置getIconImage:地点];
    dispatch_async(dispatch_get_main_queue(),^ {
        当后台任务完成后在主线程上执行// code
        如果(iconImage){
            [iconImgView setImage:iconImage.UIImage];
        }
    });
});

getIconImage 方法处理基地SVG的初始加载,它读取它同步[NSInputStream inputStreamWithFileAtPath:路径] 如果地方和 [NSURLConnection的sendSynchronousRequest:请求returningResponse:安培;响应错误:放大器; errorWithNSData] 是否应该远程加载。这一切都发生同步。

再有就是重新着色的SVG的一些处理后,它就会回来,放在的UIImageView 在主线程之前。

问:

有没有办法来组织我的code,允许并行后台加载,而是因为太多的线程prevent僵局?

解决方案编辑:

  _iconOperationQueue = [[NSOperationQueue的alloc]初始化];
_iconOperationQueue.maxConcurrentOperationCount = 8;// code将在后台执行
[_iconOperationQueue addOperationWithBlock:^ {
    // I / O code
    SVGKImage * baseIcon = [设置getIconBaseSVG:地点];    // CPU-仅code
    SVGKImage * iconImage = [设置getIconImage:位置withBaseSVG:baseIcon];
    *的UIImage = SVGImage的iconImage.UIImage; //转换SVGKImage到UIImage的价格昂贵,所以不这样做在主线程
    [NSOperationQueue mainQueue] addOperationWithBlock:^ {
        当后台任务完成后在主线程上执行// code
        如果(SVGImage的){
            [iconImgView setImage:SVGImage的]。
        }
    }];
}];


解决方案

而不是直接使用GCD与并发队列,使用 NSOperationQueue 。设置它的 maxConcurrentOperationCount 来的东西合理,喜欢4或8。

从单纯的计算

如果你可以,你也应该独立的I / O。使用宽度限制操作队列的I / O。纯计算您可以使用不受限制的操作队列或纯GCD。

原因在于I / O模块。 GCD检测系统空闲和旋转起来另一个工作线程和从队列中启动另一个任务。在我的块/ O,也因此它更多一些,直到达到其极限。然后,I / O完成开始和任务解除。现在你已经超额认购的系统资源(CPU即),因为在飞行比内核更多的任务,突然他们实际使用的CPU,而不是被阻塞I / O。

因为GCD看到,该系统实际上是繁忙,直到较早的完成不会出列更多的任务纯计算任务不会引起这种问题。

The problem:

When lazy-loading a list of 100+ icons in the background, I hit the GCD thread limit (64 threads), which causes my app to freeze up with a semaphore_wait_trap on the main thread. I want to restructure my threading code to prevent this from happening, while still loading the icons asynchronous to prevent UI blocking.

Context:

My app loads a screen with SVG icons on it. The amount differs on average from 10-200. The icons get drawn by using a local SVG image or a remote SVG image (if it has a custom icon), then they get post-processed to get the final image result.

Because this takes some time, and they aren't vital for the user, I want to load and post-process them in the background, so they would pop in over time. For every icon I use the following:

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];
        }
    });
});

The getIconImage method handles the initial loading of the base SVG, which reads it synchronized with [NSInputStream inputStreamWithFileAtPath:path] if local, and [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&errorWithNSData] if it should load remotely. This all happens synchronous.

Then there is some post-processing of recoloring the SVG, before it gets returned and put in the UIImageView on the main thread.

Question:

Is there a way to structure my code to allow for parallelized background loading but prevent deadlock because of too many threads?

Solution EDIT:

_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];
        }
    }];
}];

解决方案

Instead of directly using GCD with a concurrent queue, use an NSOperationQueue. Set its maxConcurrentOperationCount to something reasonable, like 4 or 8.

If you can, you should also separate I/O from pure computation. Use the width-restricted operation queue for the I/O. The pure computation you can use an unrestricted operation queue or pure GCD for.

The reason is that I/O blocks. GCD detects that the system is idle and spins up another worker thread and starts another task from the queue. That blocks in I/O, too, so it does that some more until it hits its limit. Then, the I/O starts completing and the tasks unblock. Now you have oversubscribed the system resources (i.e. CPU) because there are more tasks in flight than cores and suddenly they are actually using CPU instead of being blocked by I/O.

Pure computation tasks don't provoke this problem because GCD sees that the system is actually busy and doesn't dequeue more tasks until earlier ones have completed.

这篇关于如何并行,没有击中全球GCD限制多(100+)的任务?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆