【问题标题】:Best multithreading approach in Objective C?Objective C 中最好的多线程方法?
【发布时间】:2012-07-15 22:10:03
【问题描述】:

我正在开发一款 iPad 应用,目前正在努力寻找最佳的多线程方法。让我用一个简化的例子来说明这一点:
我有一个包含 2 个子视图的视图、一个目录选择器和一个画廊,其中包含所选目录中所有图像的缩略图。由于“下载”和生成这些缩略图可能需要很长时间,所以我需要多线程,这样视图的交互和更新就不会被阻塞。

这是我已经尝试过的:
[self performSelectorInBackground:@selector(displayThumbnails:) withObject:currentFolder];
这很好用,因为用户交互没有被阻止,但是当用户在第一个文件夹仍在加载时点击另一个文件夹时,它会惨遭失败。两个线程试图访问相同的视图和变量,这会导致彼此之间的正确执行混乱。当用户点击另一个文件夹时,当前加载文件夹的displayThumbnails 应该被中止。我没有找到任何方法来做到这一点..

NSThreads
我尝试了这个,但遇到了与第一种方法几乎相同的问题,我没有找到一种(简单的)方法来取消正在进行的方法。 (是的,我知道[aThread cancel],但没有找到“恢复”线程的方法)。也许我应该继承NSThread 并实现我自己的 isRunning 等方法?但是有没有更好的方法或我忽略的第三个(甚至第四个和第五个)选项?

我认为这是一个相当简单的示例,并且我认为在不继承 NSThread 的情况下可能有更好的解决方案。那么,你会怎么做?请发表您的意见!

【问题讨论】:

  • 显然我无法做出只显示“GCD”的答案
  • 你完全错了。说真的。
  • 查看here 为什么 GCD 如此酷 :) 或者更好的是,观看其中一个 WWDC 会议 :) 关于您的原始问题:有多种取消或暂停方式,无论您是否重新使用线程、GCD 或 NSOperationQueue。您可以休眠、挂起、等待锁定、完全取消等。我认为 NSOperationQueue 现在对您来说是一个很好的解决方案,因为它会自动引导您进行合理的实现..
  • Apple 的 Threading Programming Guide 在其首页将您定向到 Concurrency Programming Guide,这正是您想要的。
  • .. 但也一定要研究其他技术! WWDC 会议视频或Concurrency Guide on Apple's Developer Website 是很好的起点。

标签: objective-c ios multithreading cocoa nsthread


【解决方案1】:

NSOperationQueue 应该可以很好地完成这项任务。

另一种选择是简单的GCD,但是,如果您从未使用过它,NSOperationQueue 可能是更好的选择,因为它几乎会自动引导您以“正确的方式”实现事物,有明显的取消方式等。

【讨论】:

    【解决方案2】:

    您想使用并发 NSOperations 在后台下载和处理图像。这些将由 NSOperationsQueue 管理。本质上,这些操作将被配置为每次操作获取一张图像,对其进行处理,将其保存在文件系统中,然后在主线程中向主应用程序发送消息,告知图像可用。

    您可以查看 github 上的几个项目,这些项目展示了如何执行此操作 - 只需使用“Concurrent”或“NSOperation”搜索 github。

    iOS 有一个非常好的工具来做后台工作。 Grand Central Dispatch (GCD) 和 Blocks,但它们不允许您使用委托回调来拥有对象 - 因此是 NSOperation。

    因此,您需要阅读块、GCD,然后查看一些开源 Concurrent NSOperations 代码。使用 Concurrent NSOperations 并不像使用块那么简单。

    【讨论】:

      【解决方案3】:

      如果我遇到这个问题,我可能会采用这样的方法:

      • 将加载图像并导致主线程显示结果的单个线程(我不喜欢让线程与 GUI 对象混淆)

      • 当请求一个新目录时...嗯,这取决于你想如何管理事物。基本上,一个标准的队列结构(条件变量和数组)可以用于主线程,通过传递路径名来告诉线程“将需要这个目录”;即使在加载图像时(例如在每张图像之后),线程也会检查队列,并在出现时切换到新目录

      • 您可以创建一个目录读取器对象来保存所有状态,并将其通过路径索引存储到字典中。当请求一个新目录时,首先检查该字典,如果该目录没有,则只创建一个新对象。这样,部分加载的目录会一直存在,直到再次需要它们,并且可以继续加载,而不必从头开始。

      线程的伪代码:

      while (forever)
         new element = nil
         if we have an active directory loader
             tell directory loader to load one image
             if false then make directory loader inactive
             lock queue condition
             if queue has elements
                new element = retrieve LAST element (we aren't interested in the others)
                empty queue
                unlock with status "empty"
             else
                unlock queue
         else
             lock queue on condition "has elements"
             new element = retrieve last element
             empty queue
             unlock with status "empty"
         if new element != nil
             if directory loader for new path does not exist
                setup new directory loader for new path
                store in dictionary
                make it the "active" one
             else
                make the current one the "active"
      

      至于目录加载器,它可能看起来像这样:

      read one image:
         if there are still images to read:
             read, process and store one
             return true
         else
             performSelectorOnMainThread with an "update GUI" method and the image list as parameter
             return false;
      

      这只是一个速写;线程中有一些代码重复,我编写它的方式只会在读取所有图像后更新 GUI,而不是在我们读取它们时显示它们。您必须复制当前图像列表,或添加同步(如果您想这样做)。

      【讨论】:

        猜你喜欢
        • 2017-12-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-14
        • 1970-01-01
        • 1970-01-01
        • 2011-06-24
        相关资源
        最近更新 更多