【问题标题】:How to programmatically control and balance a number of threads iOS app is executing?如何以编程方式控制和平衡 iOS 应用程序正在执行的多个线程?
【发布时间】:2013-01-11 04:50:48
【问题描述】:

如何控制和平衡我的应用正在执行的线程数量,如何限制它们的数量以避免应用因为达到线程限制而阻塞?

在这里,我看到了以下可能的答案:“主并发队列(dispatch_get_global_queue)自动管理线程数”,我不喜欢,原因如下:

考虑以下模式(在我的真实应用中,有更简单和更复杂的示例):

dispatch_queue_t defaultBackgroundQueue() {
    return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
}

dispatch_queue_t databaseQueue() {
    dispatch_queue_create("Database private queue", 0);
}

dispatch_async(defaultBackgroundQueue(), ^{
    [AFNetworkingAsynchronousRequestWithCompletionHandler:^(data){
        dispatch_async(databaseQueue(), ^{
            // data is about 100-200 elements to parse
            for (el in data) {

            }

            maybe more AFNetworking requests and/or processing in other queues or

            dispatch_async(dispatch_get_main_queue(), ^{
                // At last! We can do something on UI.
            });
        });
    }];
});

这种设计经常导致以下情况:

  1. 应用因线程数达到限制而被锁定(类似于 > 64)
  2. 较慢且因此较窄的队列可能会因大量挂起的作业而不堪重负。
  3. 第二个也可能产生取消问题 - 如果我们有 100 个作业已经在串行队列中等待执行,我们无法立即取消它们。

显而易见且愚蠢的解决方案是将敏感的 dispatch_async 方法替换为 dispatch_sync,但它绝对是我不喜欢的方法。

对于这种情况,推荐的方法是什么?

我希望确实存在一个比“使用 NSOperationQueue - 它可以限制并发操作的数量”更聪明的答案(类似主题:Number of threads with NSOperationQueueDefaultMaxConcurrentOperationCount)。

更新 1:唯一体面的模式是:是将所有 dispatch_async 块替换为并发队列,并在基于 NSOperationQueue 的并发队列中运行包装在 NSOperations 中的这些块,并设置最大操作限制(在我的情况下,也可能设置一个最大值AFNetworking 在其中运行所有操作的基于 NSOperationQueue 的队列的操作限制)。

【问题讨论】:

  • “应用被锁定”是什么意思?你总会有一些线程在执行,只要有一些线程在执行,你的应用就会取得进展。
  • “不知所措”是什么意思?如果您提交作业的速度快于 CPU 执行它们的速度,那么无论它们有多宽,队列都会积压。
  • 使用您使用的方法(或 NSOperation,就此而言),显式线程的创建和触发超出了范围。我不一定同意问题如您所想 - 但是,在回答这个问题时,您可以显式控制创建的线程数量的唯一方法是手动创建它们,例如使用 NSThread。
  • 只需加上我的 0.05 美元:外部的 dispatch_async(defaultBackgroundQueue(), ^{...} 是不必要的,AFNetworking 有自己的队列,用于执行 HTTP 请求。启动该请求的过程非常快,没有理由将其包装在异步块中。

标签: ios objective-c concurrency


【解决方案1】:

您启动的网络请求过多。 AFAIK 它没有在任何地方记录,但您最多可以同时运行 6 个网络连接(考虑到RFC 2616 8.1.4,第 6 段,这是一个合理的数字)。之后你得到锁定,GCD 补偿创建更多线程,顺便说一下,have a stack space of 512KB each 按需分配页面。所以是的,为此使用 NSOperation。我用它来排队网络请求,在再次请求相同对象时增加优先级,如果用户离开,则暂停并序列化到磁盘。我还监控网络请求的速度(以字节/时间为单位)并更改并发操作的数量。

【讨论】:

  • 你先生是个传奇。我喜欢这个解释非常有帮助。
【解决方案2】:

虽然我从您的示例中看不到您创建“太多”后台线程的确切位置,但我将尝试回答如何控制每个队列的确切线程数的问题。苹果的documentation 说:

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

虽然您现在(从 iOS5 开始)可以手动创建并发队列,但无法控制这样一个队列将同时运行多少作业。操作系统会自动平衡负载。如果出于某种原因,您不希望这样,例如,您可以手动创建一组 n 串行队列并将新作业分派到您的 n 队列之一随机:

NSArray *queues = @[dispatch_queue_create("com.myapp.queue1", 0),dispatch_queue_create("com.myapp.queue2", 0),dispatch_queue_create("com.myapp.queue3", 0)];
NSUInteger randQueue = arc4random() % [queues count];
dispatch_async([queues objectAtIndex:randQueue], ^{
    NSLog(@"Do something");
});
randQueue = arc4random() % [queues count];
dispatch_async([queues objectAtIndex:randQueue], ^{
    NSLog(@"Do something else");
});

我绝不赞同这种设计——我认为并发队列非常擅长平衡系统资源。但是既然你问了,我认为这是一种可行的方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-28
    相关资源
    最近更新 更多