7

我有一个图像加载器类,它提供了来自网络的 NSURL 加载和图像并执行完成块。代码其实很简单

- (void)downloadImageWithURL:(NSString *)URLString completion:(BELoadImageCompletionBlock)completion
{
    dispatch_async(_queue, ^{
//    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        UIImage *image = nil;
        NSURL *URL = [NSURL URLWithString:URLString];
        if (URL) {
            image = [UIImage imageWithData:[NSData dataWithContentsOfURL:URL]];
        }  
        dispatch_async(dispatch_get_main_queue(), ^{
            completion(image, URLString);
        });
    });

}

当我更换

dispatch_async(_queue, ^{

注释掉

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{

图像加载速度更快,这是非常合乎逻辑的(在图像一次加载一个之前,现在一堆同时加载)。我的问题是我可能有 50 张图片,我为所有图片调用了 downloadImageWithURL:completion: 方法,当我使用全局队列而不是 _queue 时,我的应用程序最终崩溃,我看到有 85 个以上的线程。问题可能是我连续 50 次调用 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0) 会使 GCD 创建太多线程吗?我认为 gcd 处理所有的线程并确保线程数不是很大,但如果不是这样,有什么办法可以影响线程数吗?

4

3 回答 3

10

当全局并发队列的现有 GCD 工作线程上的工作单元在内核中被阻塞很长时间(只要全局队列上还有待处理的工作),内核会创建额外的线程。

这是必要的,以便应用程序可以继续总体上取得进展(例如,待处理块之一的执行可能是允许被阻塞线程被解除阻塞的原因)。

如果工作线程在内核中被阻塞的原因是 IO(例如+[NSData dataWithContentsOfURL:]在这个例子中),最好的解决方案是用一个 API 替换那些调用,该 API 将异步执行该 IO 而不会阻塞,例如NSURLConnection用于网络或调度 I/O文件系统 IO。

或者,您可以手动限制并发阻塞操作的数量,例如通过使用计数调度信号量。

WWDC 2012 GCD 会议详细讨论了这个主题。

于 2013-02-05T06:14:11.693 回答
3

来自http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html

并发队列(也称为全局调度队列的一种)同时执行一个或多个任务,但任务仍然按照它们添加到队列的顺序启动。当前正在执行的任务在由调度队列管理的不同线程上运行。在任何给定点执行的任务的确切数量是可变的,并且取决于系统条件。

串行队列(也称为私有调度队列)按添加到队列的顺序一次执行一项任务。当前正在执行的任务在由调度队列管理的不同线程上运行(可能因任务而异)。

通过将所有块分派到高优先级并发调度队列

[NSData dataWithContentsOfURL:URL]

这是一个同步阻塞网络操作,看起来默认的 GCD 行为将产生大量线程以尽快执行您的块。

你应该派往DISPATCH_QUEUE_PRIORITY_BACKGROUND. 这些任务绝不是“高优先级”。任何图像处理都应该在有空闲时间并且主线程上没有任何事情发生时进行。

如果您想更好地控制一次发生多少这些事情,我建议您考虑使用NSOperation. 您可以使用您的块并将它们嵌入到操作中NSBlockOperation,然后您可以将这些操作提交给您自己的NSOperationQueue. 如果NSOperationQueue需要- (NSInteger)maxConcurrentOperationCount,还可以在调度后取消操作。

于 2013-02-04T12:01:14.303 回答
1

您可以使用NSOperationqueue,它支持NSURLConnection

它具有以下实例方法:

- (void)setMaxConcurrentOperationCount:(NSInteger)count
于 2013-02-04T11:49:28.423 回答