- What are the differences between Operation Queue and Dispatch Queue?
- Under what circumstances will it be more appropriate to use each?
OperationQueue
internally uses Grand Central Dispatch and on iOS.
OperationQueue
gives you a lot more control over how your operations are executed. You can define dependencies between individual operations for example, which isn't possible with plain GCD queues. It is also possible to cancel operations that have been enqueued in an OperationQueue
(as far as the operations support it). When you enqueue a block in a GCD dispatch queue, it will definitely be executed at some point.
To sum it up, OperationQueue
can be more suitable for long-running operations that may need to be cancelled or have complex dependencies. GCD dispatch queues are better for short tasks that should have minimum performance and memory overhead.
- Prefer GCD where task is not much complex and optimum CPU performance is required.
- Prefer NSOperationQueue where task is complex and requires canceling or suspending a block and dependency management.
GCD is a lightweight way to represent units of work that are going to be executed concurrently. You don’t schedule these units of work; the system takes care of scheduling for you. Adding dependency among blocks can be a headache. Canceling or suspending a block creates extra work for you as a developer!
NSOperation and NSOperationQueue add a little extra overhead compared to GCD, but you can add dependency among various operations. You can re-use operations, cancel or suspend them. NSOperation is compatible with Key-Value Observation (KVO); for example, you can have an NSOperation start running by listening to NSNotificationCenter.
NSOperation and NSOperationQueue are higher lever APIs, made on top of the GDC itself, to achieve the concurrency in an object oriented way.
One common misconception about GCD is that “once you schedule a task it can’t be canceled, you need to use the Operation API for that”. With iOS 8 & macOS 10.10 DispatchWorkItem was introduced, which provides this exact functionality in an easy to use API.
As I read in Apple developer documentation for DispatchQueue, now you can cancel your task from execution. For that, you have to work with DispatchWorkItem while using GCD over OperationQueue.
-
A dispatch work item has a cancel flag. If it is cancelled before running, the dispatch queue won’t execute it and will skip it. If it is cancelled during its execution, the cancel property return true. In that case, we can abort the execution. Also work items can notify a queue when their task is completed.
Note: GCD doesn’t perform preemptive cancelations. To stop a work item that has already started, you have to test for cancelations yourself.
As in below example, I checked like the following code
if (task?.isCancelled)! {
break
}
Definition by Apple
A DispatchWorkItem encapsulates work to be performed on a dispatch queue or within a dispatch group. You can also use a work item as a DispatchSource event, registration, or cancellation handler.
I took the below example from SwiftIndia's Medium post. For more details please follow Apple documentation and SwiftIndia's 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")
© 2022 - 2024 — McMap. All rights reserved.