iOS应用程序的操作队列与调度队列

时间:2011-08-16 13:07:15

标签: iphone objective-c ios cocoa-touch grand-central-dispatch

  1. Operation Queue和Dispatch Queue之间有什么区别?
  2. 在什么情况下使用它们更合适?

3 个答案:

答案 0 :(得分:52)

NSOperationQueue早于Grand Central Dispatch,在iOS上它不使用GCD执行操作(这在Mac OS X上有所不同)。它使用常规后台线程,其开销比GCD调度队列多一点。

另一方面,NSOperationQueue使您可以更好地控制操作的执行方式。例如,您可以定义各个操作之间的依赖关系,这对于普通GCD队列是不可能的。也可以取消已在NSOperationQueue中排队的操作(只要操作支持它)。当您将一个块排入GCD调度队列时,它肯定会在某个时刻执行。

总而言之,NSOperationQueue可能更适合可能需要取消或具有复杂依赖关系的长时间运行操作。对于应该具有最低性能和内存开销的短任务,GCD调度队列更好。

编辑:似乎有关NSOperationQueue的文档已过时且 在iOS 4.0及更高版本上使用GCD。

答案 1 :(得分:18)

  • 首选GCD,其中任务不复杂,需要最佳CPU性能
  • 首选NSOperationQueue,其中任务很复杂,需要取消或暂停阻止和依赖关系管理

GCD是一种表示将同时执行的工作单元的轻量级方法。你没有安排这些工作单位;系统负责为您安排日程安排。在块之间添加依赖性可能是令人头疼的问取消或暂停某个块会为开发人员创建额外的工作!

与GCD相比,NSOperation和NSOperationQueue增加了一些额外开销,但您可以在各种操作之间添加依赖关系。您可以重复使用操作,取消或暂停它们。 NSOperation与Key-Value Observation(KVO)兼容;例如,您可以通过侦听NSNotificationCenter来运行NSOperation。

NSOperation和NSOperationQueue是更高级的杠杆API,在GDC本身之上制作,以面向对象的方式实现并发。

For detailed explanation, refer this question: https://stackoverflow.com/questions/10373331/nsoperation-vs-grand-central-dispatch

答案 2 :(得分:0)

关于GCD的一个常见误解是“一旦安排了无法取消的任务,就需要使用Operation API” 。在iOS 8和macOS 10.10中,引入了 DispatchWorkItem ,它通过易于使用的API提供了此确切功能。

正如我在 Apple开发人员文档中阅读的 DispatchQueue 一样,现在您可以取消执行任务。为此,您必须在通过OperationQueue使用GCD时使用 DispatchWorkItem

  •   

    调度工作项具有取消标志。如果之前取消   在运行中,调度队列将不会执行,而是会跳过。如果它   在执行过程中被取消,cancel属性返回true。在   在这种情况下,我们可以中止执行。工作项目也可以通知   他们的任务完成时排队。

注意:GCD不会执行抢先取消。要停止已经开始的工作项,您必须自己测试取消情况。

在下面的示例中,我检查了以下代码

if (task?.isCancelled)! {
      break;
   }

Apple的定义

  

DispatchWorkItem封装了要在调度上执行的工作   队列或调度组内。您还可以将工作项用作   DispatchSource事件,注册或取消处理程序。

我从 SwiftIndia's Medium post 中获取了以下示例。有关更多详细信息,请关注Apple documentation和SwiftIndia的Medium Post。

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let concurrentQueue = DispatchQueue(label: "com.queue.Concurrent", attributes: .concurrent)

func performAsyncTaskInConcurrentQueue() {
    var task:DispatchWorkItem?

    task = DispatchWorkItem {
        for i in 1...5 {
            if Thread.isMainThread {
                print("task running in main thread")
            } else{
                print("task running in other thread")
            }

            if (task?.isCancelled)! {
                break
            }

            let imageURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
            let _ = try! Data(contentsOf: imageURL)
            print("\(i) finished downloading")
        }
        task = nil
    }

    /*
     There are two ways to execute task on queue. Either by providing task to execute parameter or
     within async block call perform() on task. perform() executes task on current queue.
     */
    // concurrentQueue.async(execute: task!)

    concurrentQueue.async {
        task?.wait(wallTimeout: .now() + .seconds(2))
        // task?.wait(timeout: .now() + .seconds(2))
        task?.perform()
    }
    concurrentQueue.asyncAfter(deadline: .now() + .seconds(2), execute: {
        task?.cancel()
    })

    task?.notify(queue: concurrentQueue) {
        print("\n############")
        print("############")
        print("###### Work Item Completed")
    }
}

performAsyncTaskInConcurrentQueue()

print("###### Download all images asynchronously and notify on completion ######")
print("############")
print("############\n")