Swift Basics: Concurrency and Parallelism - running multiple tasks

Concurrency - many tasks starting, running, and completing at the same time period.
Parallelism - 2 or more tasks running simultaneously.
If we have a 4-core processor, we can run 4 tasks simultaneously.
Asynchronous functions are functions that run in the background. These functions might take a long time to complete.
It starts with the long task running and comes back before the task's completion.
To have asynchronous running of tasks, we can make use of GCD and operation queues.
GCD stands for Grand Central Dispatch.
There are three types of queues that it uses.
Serial queue - executed in the order in which they are submitted. No two tasks will run simultaneously.
Concurrent queue - execute concurrently, but start according to the time they are added to the queue.
Main dispatch queues - main thread.

With dispatch queues, threads are managed efficiently and we can control the order in which they start.

Let's say I have a function called Countdown(timing: Int).
How can it work in various queues?

First, create the queue:
let concurrentQueue = DispatchQueue(label: "givea.unique.labelname", attributes: .concurrent)
OR
let serialQueue = DispatchQueue(label: "givea.unique.labelname")
The label can be nil.
After that, create an instance of the class with the function:
let timer = Timer()
Create a closure with the method
let c = {timer.Countdown(100)}
concurrentQueue.async(execute: c)
We can also do this:
concurrentQueue.async{timer.Countdown(100)}
We can call multiple queues with different timings:
concurrentQueue.async{timer.Countdown(10)}
Even though the first queue was called first, it will finish later.

async method will not block the current thread. The current thread will still execute as per normal.
If we need the current thread to stop, we can use the sync method.

asyncAfter method will execute blocks of code asynchronously after a given delay.

For instance:
let serialQueue = DispatchQueue(label: "givea.unique.labelname")
let delayInSeconds = 3.0
let pTime = DispatchTime.Now() + Double(Int64(delayInSeconds + Double(NSEC_PER_SEC)))/ Double(NSEC_PER_SEC)
serialQueue.asyncAfter(deadline: pTime)
{print("Time's up")}

We can also make use of the BlockOperation class, which runs tasks in a FIFO manner. The tasks can be executed concurrently if resources are available.

let bo: BlockOperation = BlockOperation.init(block:
{ timer.Countdown(1000)})
bo.addExecutionBlock({timer.Countdown(50)})
let opQ = OperationQueue()
opQ.addOperation(bo)

We can define the number of tasks that we want to be able to run at a time:
opQ.maxConcurrentOperationCount = 1

We can make use of the operation queue directly instead of using block operation:
opQ.addOperation(){ timer.Countdown(50)}
Rather than wait for all the tasks to be loaded first and for them to start at the same time, the addOperation function runs the task as soon as the function is declared.

We can create a subclass of the Operation class as well.
class MyOps: Operation
{
let timeTaken: Int
 init(timeTaken: Int){self.timeTaken = timeTaken}
override func main() { countdown()}
func countdown()
 {
 let start = CFAbsoluteTimeGetCurrent()
 for _ in 0 ..< timeTaken
  {
   let x = 100
   let y = x*x
   _ = y/x
  }
 }
}

We can add instances of the MyOps class to the operation queues:
let opQ = NSOperationQueue()
opQ.addOperation(MyOps(timeTaken: 100))
opQ.addOperation(MyOps(timeTaken: 1000))


Comments

Popular posts from this blog

Setting up a playground

Go to another page