目标C中的最佳多线程方法



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

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

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

我认为这是一个相当简单的例子,我认为可能有一个更好的解决方案,而不需要对NSThread进行子类化。那么,你会怎么做呢?请您发表意见!

NSOperationQueue应该能很好地执行此任务。

另一种选择是普通的GCD,然而,如果你从未使用过它,NSOperationQueue可能是更好的选择,因为它几乎自动引导你以"正确的方式"实现事情,有明显的取消方法等。

您希望使用Concurrent NSOperations在后台下载和处理图像。这些将由NSOperationsQueue进行管理。从本质上讲,这些操作将被配置为每个操作获取一个图像,处理它,将其保存在文件系统中,然后向主线程中的主应用程序返回图像可用的消息。

github上有几个项目可以展示如何做到这一点——只需使用"并发"或"NSOperation"搜索github即可。

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

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

如果我有这个问题,我可能会选择这样的方法:

  • 一个单独的线程,它将加载图像,并使主线程显示结果(我不太喜欢让线程处理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,而不是在读取图像时显示它们。您必须复制当前图像列表,或者如果您想添加同步,则必须添加同步。

最新更新