在IOS中并行复制文件夹

时间:2015-03-26 17:19:23

标签: ios multithreading file-io uikit

我正在尝试复制位于以下位置的2个文件夹:

foo/a
foo/b

bar/a
bar/b

我需要阻止主线程,直到两个文件夹都被复制。 另外我想不在主线程上执行文件i / o。

执行此操作的一种方法是阻止foo / a复制到bar / a然后将foo / b复制到bar / b。

我正在考虑使用2个NSOperations并并行化操作。这种方法有什么优势吗?换句话说,是否在iOS上序列化了文件操作?

4 个答案:

答案 0 :(得分:2)

  

是否在iOS上序列化了文件操作?

如果他们在同一个帖子上,是的。但是,Apple鼓励您使用多个线程进行文件操作。来自Apple Docs

  

因为与文件相关的操作涉及与硬盘交互   因此,与大多数其他操作相比,它们很慢   iOS和OS X中与文件相关的接口采用并发性设计   记住。

,特别是关于NSFileManager

  

对于大多数任务,使用默认的NSFileManager对象是安全的   同时来自多个后台线程....你应该   一次使用一个线程中的唯一实例。

所以你可以并行地在不同的线程上进行复制。

  

并行化两个NSOperations会有什么优势吗?

是。我想到的一个文件夹是一个大的低优先级文件夹,另一个文件夹是一个小而重要的文件夹。您可以在后台线程上执行这两个操作,但为关键复制操作提供更高的线程优先级。

答案 1 :(得分:1)

如果我错过了这一点,请提前道歉:

鉴于在iOS上几乎肯定有一个单一的文件系统,我不确定你会通过并行执行两个目录副本来看到任何真正的加速。它是相同的闪存芯片,相同的内存总线等。同时进行两个并行拷贝并不会使这些单个事物运行得比你在另一个之后发生的更快。没有什么可以从运行它的两个独立CPU线程中受益的硬件。

两个并行副本唯一可以改进的是驱动FS所需的CPU时间,但这肯定不会成为iOS系统的瓶颈。

如果您不希望主线程被阻止,我认为您最好使用异步I / O.我不知道iOS在该行中提供了什么,但我确信会有一些异步I / O API可用。或者,您可以让一个后台线程按顺序执行所有目录复制,从而允许主线程同时执行所需的任何操作。

答案 2 :(得分:0)

是的,您可以异步执行多个I / O操作。因此,通过将复制作为并行操作运行,您将获得一些优势。您可以显式锁定某些文件,但默认情况下,所有线程都具有与主进程(docs)相同的访问能力,并且没有任何东西阻止它们并行访问文件(即I / O操作未被序列化)。您可以查看Concurrency Programming Guide以获取有关并发文件访问的更多信息。

进一步说明,大多数系统中的文件I / O都不是非常受保护的进程。考虑到开发人员在iOS / OS X编程中使用的抽象级别,就文件访问而言,硬件的差异基本上是无关紧要的。在这个意义上,文件并没有真正受到保护例如,您可以让两个线程访问完全相同的文件,如果处理不当,可以轻松销毁该文件(more info)。

原始答案:

这涵盖了更多实施级别的详细信息。

在这种情况下,我建议只使用单个操作队列并具有非一个并发操作限制。如果您查看maxConcurrentOperationCount的文档,您将看到操作队列的并发操作计数的默认值为NSOperationQueueDefaultMaxConcurrentOperationCount,这意味着队列将尝试执行尽可能多的并行操作。

对于不依赖于彼此的块(因此需要同步运行),这显然是很好的,并且默认情况下,这种行为对您来说非常有用。

至于UI Blocking。您应该可以通过addOperations:waitUntilFinished:添加排队的复制操作,然后将YES传递给waitUntilFinished:。这将阻止当前线程,因此只需在主线程上添加块,UI就会自动阻止。然后,一旦完成所有操作(即使它们正在运行异步),主线程将再次打开。很漂亮。

答案 3 :(得分:0)

你有很多方法可以做到这一点,但我不明白你为什么要阻止主线程直到复制完成,即使你想要从主线程中复制副本。
在我看来,实现这一目标的最佳方法是使用进度/活动视图阻止该UI,因此用户无法在UI上按任何内容,因为副本我可以考虑这些选项:

  • NSOperationQueue,具有2副本操作(通过子类化NSOperation创建)之间的依赖关系,如果您希望在另一个完成后启动它并将您的类注册为-operationCount NSOperationQueue属性的观察者,当计数达到0你可以"打开"用户界面。我认为这对于这两项行动来说太过分了。 GCD将决定他们将在哪个队列中运行。
  • 使用dispatch_semaphore_t,只需在特定队列的正常dispatch_async内启动复制方法即可。更多信息here
  • 使用串行队列和GCD,使用已提供的串行队列dispatch_get_global_queue(DISPATCH_QUEUE_SERIAL, 0)。然后调用dispatch_async passig,该队列添加3个块,第一个副本,第二个副本和"打开"您的用户界面。以下是保证按照您添加的顺序执行操作的连续执行。
  • 使用GCD dispatch_group,创建一个组并添加指向它们的副本块以在除主要队列之外的另一个队列上运行,然后当这些队列完成并且"打开" UI.More信息here
  • dispatch_write与信号量一起使用,因为dispatch write是一种非常有效的异步调用。更多信息here

这些是一些想法,但在我看来,实施得更快的是dispatch_group。请记住,队列与线程的概念不同 文件系统是单一的并且是共享的我不认为你将以任何特定的速度并行运行它们,根据我的经验,我发现写入和读取文件的运行速度更快,这可能是由于异步调用的开销,因为只有一个资源。在iOS上编写和读取文件的一种非常有效的方法是dispatch_io函数,但它们可能是压倒性的 在我看来,如果你希望用户在复制文件的同时继续使用你的应用程序,那么使用异步调用是很有用的,但是因为你想要阻止UI,所以显示进度条可能会很有用,你可以实现它通过使用在主队列上启动回调的简单串行队列来更新UI。