Dispatch Semaphore examples in Swift

Dispatch Semaphore example in Swift
DispatchSemaphore Example

Hey guys,
Welcome to the Dispatch Semaphore examples in Swift post

In this post I will show you how to use Dispatch Semaphores in Swift. We will be looking at three different examples.

First we will create three operations. Each operation will count from 1 to a given number, like so:

for i in 1...taskItem{
            sleep(1)
            print("TaskItem: \(taskItem), i: \(i)")
        }

Where taskItem actually is defined as an unsigned integer array:

var sleepTaskArray = [UInt32]()
sleepTaskArray.append(3)
sleepTaskArray.append(7)
sleepTaskArray.append(2)

So all we have here is a second counting operations. Here is the full code listing so far:

var sleepTaskArray = [UInt32]()
sleepTaskArray.append(3)
sleepTaskArray.append(7)
sleepTaskArray.append(2)

for taskItem in sleepTaskArray{
    for i in 1...taskItem{
        sleep(1)
        print("TaskItem: \(taskItem), i: \(i)")
    }
}
print("Done")

If we execute the give code segment, we will get the following output:

TaskItem: 3, i: 1
TaskItem: 3, i: 2
TaskItem: 3, i: 3
TaskItem: 7, i: 1
TaskItem: 7, i: 2
TaskItem: 7, i: 3
TaskItem: 7, i: 4
TaskItem: 7, i: 5
TaskItem: 7, i: 6
TaskItem: 7, i: 7
TaskItem: 2, i: 1
TaskItem: 2, i: 2

Which is exactly what we expected. A sequential execution of the given operations.

Example 1: Sequential Dispatch Semaphore examples in Swift

The first example is the easiest. Let’s use Dispatch Semaphore and implement the same solution.

var sleepTaskArray = [UInt32]()
sleepTaskArray.append(3)
sleepTaskArray.append(7)
sleepTaskArray.append(2)

let semaphote = DispatchSemaphore(value: 0)
let queue = OperationQueue()
for taskItem in sleepTaskArray{
    queue.addOperation {
        for i in 1...taskItem{
            sleep(1)
            print("TaskItem: \(taskItem), i: \(i)")
        }
        semaphote.signal()
    }
    semaphote.wait()
}
queue.waitUntilAllOperationsAreFinished()
print("Done")

As you can see we are now using a Dispatch Semaphore. The initial counter for the semaphore is set to 0. What you need to know is that if the counter gets a value bellow zero it freezes the execution thread.

In order to launch the counting operation we are going to use an OperationQueue. You can look up the documentation here.

Now that we have the code in place, let’s discuss the soltion.

First we loop through the SleepTaskArray. After that, for each we launch a concurrent operation block, which will execute outside of the execution thread. The moment operation block starts executing we call: semaphore.wait() which decrements the counter from zero to -1, and the main thread freezes. When the background counting operation is finished executing it will call semaphore.signal(), which in turn it will increment the semaphore counter by one. Therefore, the executing thread will continue it’s execution because the semaphore counter is set to zero. In conclusion, we will get the following output:

TaskItem: 3, i: 1
TaskItem: 3, i: 2
TaskItem: 3, i: 3
TaskItem: 7, i: 1
TaskItem: 7, i: 2
TaskItem: 7, i: 3
TaskItem: 7, i: 4
TaskItem: 7, i: 5
TaskItem: 7, i: 6
TaskItem: 7, i: 7
TaskItem: 2, i: 1
TaskItem: 2, i: 2

Example 2: Concurrent Dispatch Semaphore examples in Swift

Let’s see how we can execute the tasks in parallel. After that, I’ll explain the difference.

var sleepTaskArray = [UInt32]()
 sleepTaskArray.append(3)
 sleepTaskArray.append(7)
 sleepTaskArray.append(2) 

 let queue = OperationQueue()
 for taskItem in sleepTaskArray{
     queue.addOperation {
         for i in 1…taskItem{
             sleep(1)
             print("TaskItem: (taskItem), i: (i)")
         }
     }
 }
 queue.waitUntilAllOperationsAreFinished()
 print("Done")

As you can see in this solution we do not use the Dispatch Semaphore (although we can). However, I did use the queue.waitUntillAllOperationsAreFinished() function. In other words, this function will stop the execution thread until all operations are done.

Let’s look at the output:

TaskItem: 2, i: 1
TaskItem: 7, i: 1
TaskItem: 3, i: 1
TaskItem: 2, i: 2
TaskItem: 3, i: 2
TaskItem: 7, i: 2
TaskItem: 3, i: 3
TaskItem: 7, i: 3
TaskItem: 7, i: 4
TaskItem: 7, i: 5
TaskItem: 7, i: 6
TaskItem: 7, i: 7

Great, that’s exactly what we wanted.

Example 3: Concurrent Dispatch with more control

The problem with the previous solution is that we have to wait for all tasks to finish before moving on. What If I want a different task execution schedule?

What If I want the same result using 2 threads? Is that possible?

Let’s look at the following code:

var sleepTaskArray = [UInt32]()
sleepTaskArray.append(3)
sleepTaskArray.append(7)
sleepTaskArray.append(2)

let semaphote = DispatchSemaphore(value: 1)
let queue = OperationQueue()
for taskItem in sleepTaskArray{
    queue.addOperation {
        for i in 1...taskItem{
            sleep(1)
            print("TaskItem: \(taskItem), i: \(i)")
        }
        semaphote.signal()
    }
    semaphote.wait()
}
queue.waitUntilAllOperationsAreFinished()
print("Done")

The plan here is to use two threads at a time. Imagine the following scenario:

Thread one counts from 1 to 7. In addition, we can have a second thread counting from 1 to 3. Now the moment second thread is done, then we can spawn a new thread to count from 1 to 2. After that, we just wait the first thread to finish it’s count from 1 to 7.

The trick here is to initialize the DispatchSemaphore with initial value of 1. This is because when two threads start to run the value will be set to -1. That will cause the execution thread to freeze, until one of the threads gets free and it is able to take on another operation.

Let’s take a look at the output:

TaskItem: 3, i: 1
TaskItem: 7, i: 1
TaskItem: 7, i: 2
TaskItem: 3, i: 2
TaskItem: 3, i: 3
TaskItem: 7, i: 3
TaskItem: 7, i: 4
TaskItem: 2, i: 1
TaskItem: 7, i: 5
TaskItem: 2, i: 2
TaskItem: 7, i: 6
TaskItem: 7, i: 7

This technique allows us to have more control over the execution.

Take the time and look into my other posts:

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to Top