Situation:
I have 2 tasks says T1 & T2 in async background mode. T2 depends on T1 and have successBlock which is executes after the completion of the both tasks T1 & T2.
Quick diagram is below for better understanding.
Edit:
To better understanding the tasks, you can assume T1 and T2 are the API calls which always be going to execute in async mode. I need some output data from T1 to hit T2 API. After the completion of the both tasks I need to update UI.
To accomplish this scenario, I have added my first async work in T1 and second work in T2 and dependency of T2 to T1 and successblock have dependency on both tasks.
Code Work
My Tasks
class TaskManager {
static let shared = TaskManager()
func task1Call(complete: @escaping ()->()) {
DispatchQueue.global(qos: .background).async {
for i in 0...10 {
print("~~> Task 1 Executing ..", i)
sleep(1)
}
complete()
}
}
func task2Call(complete: @escaping ()->()) {
DispatchQueue.global(qos: .background).async {
for i in 0...10 {
print("==> Task 2 Executing ..", i)
sleep(1)
}
complete()
}
}
}
Execute Tasks
class Execution {
// Managing tasks with OperationQueue
func executeTaskWithOperation() {
let t1 = BlockOperation {
TaskManager.shared.task1Call {
print("Task 1 Completed")
}
}
let t2 = BlockOperation {
TaskManager.shared.task2Call {
print("Task 2 Completed")
}
}
let successBlock = BlockOperation {
print("Tasks Completed")
}
let oper = OperationQueue()
t2.addDependency(t1)
successBlock.addDependency(t2)
successBlock.addDependency(t1)
oper.addOperations([t1, t2, successBlock], waitUntilFinished: true)
}
}
let e = Execution()
e.executeTaskWithOperation()
Issue:
Both tasks are executing parallelly and successBlock executes before the completion of task 1 and task 2.
Console Output:
==> Task 2 Executing .. 0
Tasks Completed
~~> Task 1 Executing .. 0
~~> Task 1 Executing .. 1
==> Task 2 Executing .. 1
==> Task 2 Executing .. 2
~~> Task 1 Executing .. 2
==> Task 2 Executing .. 3
~~> Task 1 Executing .. 3
==> Task 2 Executing .. 4
~~> Task 1 Executing .. 4
==> Task 2 Executing .. 5
~~> Task 1 Executing .. 5
==> Task 2 Executing .. 6
~~> Task 1 Executing .. 6
==> Task 2 Executing .. 7
~~> Task 1 Executing .. 7
==> Task 2 Executing .. 8
~~> Task 1 Executing .. 8
==> Task 2 Executing .. 9
~~> Task 1 Executing .. 9
~~> Task 1 Executing .. 10
==> Task 2 Executing .. 10
Task 1 Completed
Task 2 Completed
I unable to figure out what wrong I am doing, even same code work fines when I use sync mode instead of async.
After Joshua's comment , I able to conclude the answer.
Execution changed from OperationQueue
to DispatchGroup
and DispatchSemaphore
.
DispatchGroup : It makes sure both task tasks are done and then it calls notify
block.
DispatchSemaphore : It holds the async resource with wait command until we wont send the signal command i.e. we are saying to semaphore to hold yourself until the task1 is not completed.
Sample code of tasks.
class Execution {
// Managing tasks with DispatchGroup
func executeTaskWithGroup() {
let groups = DispatchGroup()
let semaphore = DispatchSemaphore(value: 1)
groups.enter()
semaphore.wait()
TaskManager.shared.task1Call {
groups.leave()
semaphore.signal()
}
groups.enter()
TaskManager.shared.task2Call {
groups.leave()
}
groups.notify(queue: DispatchQueue.global(qos: .background)) {
print("Tasks Completed")
}
}
}
To execute command all we need to do is.
let e = Execution()
e.executeTaskWithGroup()
But above code is executed in the main thread and block the UI. To prevent this you need to call above piece of code in background queue like below.
let queue = DispatchQueue.init(label: "MyQueue", qos: .background, attributes: .concurrent, autoreleaseFrequency: .workItem, target: nil)
queue.async {
let e = Execution()
e.executeTaskWithGroup()
}
Now everything works fine as per my needed.
AddOn
In case, if someone requirement is to call multiple API along with the above scenario then add your tasks in async in the queue.
let queue = DispatchQueue.init(label: "MyQueue", qos: .background, attributes: .concurrent, autoreleaseFrequency: .workItem, target: nil)
queue.async {
let e1 = Execution()
e1.executeTaskWithGroup()
}
queue.async {
let e2 = Execution()
e2.executeTaskWithGroup()
}
Now both e1 and e2 executes parallelly without blocking main thread.
References :