【问题标题】:Using a single shared background thread for iOS data processing?使用单个共享后台线程进行 iOS 数据处理?
【发布时间】:2011-11-14 17:17:10
【问题描述】:

我有一个应用程序,我在其中从网络下载大量资源,并对每个资源进行一些处理。我不希望这项工作发生在主线程上,但它非常轻量级和低优先级,所以所有这些都可以真正发生在同一个共享工作线程上。这样做似乎是一件好事,因为设置和拆除所有这些工作线程(其中没有一个工作线程会持续很长时间,等等)需要做一些工作。

但令人惊讶的是,似乎没有一种简单的方法可以让所有这些工作发生在一个共享的线程上,而不是为每个任务生成一个新线程。由于多年来似乎出现了大量实现并发的途径,这使情况变得复杂。 (显式NSThreadsNSOperationQueue、GCD 等)

我是否高估了产生所有这些线程所涉及的开销?我是否应该不费吹灰之力,并使用更简单的每个任务线程方法?使用 GCD,并假设它在线程(重)使用方面比我更聪明?

【问题讨论】:

  • 不知道为什么这不简单?在具有全局线程的对象上使用performSelector:onThread:withObject:waitUntilDone: 对我来说似乎很简单?您在寻找更简单的方法吗?
  • 这很简单,前提是您已经努力创建线程并设置某人在其上发送NSRunloop
  • 没错。那里的复杂性在于线程的护理和馈送,而不是如何发送它。

标签: ios multithreading nsurlconnection grand-central-dispatch nsoperationqueue


【解决方案1】:

使用 GCD — 这是当前的官方推荐,它比任何其他解决方案都省力。如果你明确需要你传入的东西串行发生(即,好像在单个线程上),那么你可以实现这一点,但仅仅改变可能更聪明,例如

[self doCostlyTask];

收件人:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^()
{
    [self doCostlyTask];

    dispatch_async(dispatch_get_main_queue(), ^()
    {
        // most UIKit tasks are permissible only from the main queue or thread,
        // so if you want to update an UI as a result of the completed action,
        // this is a safe way to proceed
        [self costlyTaskIsFinished];
    });
});

这实质上是告诉操作系统“以低优先级执行此代码,只要执行此操作最有效”。您发布到任何全局队列的各种事物可能会或可能不会在彼此相同的线程上执行,并且作为分派它们的线程可能会或可能不会同时发生。操作系统应用它认为最佳的规则。

博览会:

GCD 是 Apple 的线程池实现,他们同时引入了闭包(作为“块”)以使其可用。所以^(C-style args){code} 语法是一个块/闭包。也就是说,它是代码加上代码引用的任何变量的状态(受警告)。您可以自己存储和调用块,无需 GCD 知识或使用。

dispatch_async 是一个 GCD 函数向指定队列发出一个块。它在某个时间在某个线程上执行块,并应用未指定的内部规则以最佳方式执行此操作。它将根据您拥有的核心数量、每个核心的繁忙程度、当前对节能的考虑(可能取决于电源)、特定 CPU 的电力成本如何计算等因素来判断。

就程序员的开发而言,块将代码变成可以作为参数传递的东西。 GCD 允许您请求根据操作系统可以管理的最佳调度来执行块。块的创建和复制非常轻巧——比例如块要轻得多。 NSOperations.

GCD 超越了上述示例中的基本异步调度(例如,您可以执行并行 for 循环并等待它在一次调用中完成),但除非您有特定需求,否则它可能并不那么相关。

【讨论】:

  • FWIW,我对 GCD 的工作原理或它的好处并不感到困惑。只是它是否最终成为这项工作的正确工具。这更多是关于我对这个特定案例的额外外在知识是否超过了 GCD 的通用性。
  • 哦,好吧,GCD 希望比您可以快速构建的任何东西都更智能,并且大部分销售宣传是“它很轻;不用担心”,如果有帮助的话。我在 10.6 下做的第一个测试是全软件矩阵乘法——基本上每个块都是一小段代码,有四个乘法、三个加法和一个存储。在我的两台核心机器上,当执行数千次这样的计算时,GCD 的并行 for 循环所用的时间几乎是传统循环的一半。所以在我的测试中,它看起来确实非常轻巧。这有帮助吗?
  • 试图在“[self costlyTaskIsFinished]; }; 之后编辑它以修复缺失的 ),应该是 [self costlyTaskIsFinished]; }); " 但它不会让我编辑,因为它只是一个字符修复。哦,好吧。
  • @GreenKiwi 我猜是因为我是原作者,它让我把丢失的括号扔进去。好地方!
【解决方案2】:

但令人惊讶的是,似乎没有一种简单的方法可以获取所有 这项工作发生在单个共享线程上,而不是 为每个任务生成一个新线程。

这正是 GCD 的用途。 GCD 维护了一个线程池,可用于执行任意代码块,它负责管理该池,以便在手头的任何硬件上获得最佳结果。这避免了不断创建和销毁线程的成本,也使您不必弄清楚有多少处理器可用等。

如果您真的关心应该只使用一个线程,Tommy 提供了正确的答案,但听起来您真的只是想避免为每个任务创建一个线程。

这很复杂,因为要实现的路径很多 这些年来似乎出现了并发性。 (明确的 NSThreads、NSOperationQueue、GCD 等)

NSOperationQueue uses GCD,所以你可以使用它,如果它比直接使用 GCD 更容易的话。

使用 GCD,并假设它在线程(重)使用方面比我更聪明?

没错。

【讨论】:

    【解决方案3】:

    我会使用 NSOperationQueue 或 GCD 和配置文件。无法想象线程开销会超过网络延迟。

    NSOperationQueue 可以让您限制同时操作的数量,如果它们最终变得过于贪婪的话。事实上,如果需要,您可以将其限制为一个。

    【讨论】:

    • 确实,从挂钟的角度来看,网络延迟会淹没线程管理时间。但从系统工作的角度来看,所有在网络上的等待基本上都是免费的,而设置和拆除线程会主动消耗/竞争资源。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-08
    • 1970-01-01
    • 2023-04-10
    • 2011-06-13
    • 2016-12-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多