DispatchSemaphore

An object that controls access to a resource across multiple execution contexts through use of a traditional counting semaphore.

A dispatch semaphore is an efficient implementation of a traditional counting semaphore. Dispatch semaphores call down to the kernel only when the calling thread needs to be blocked. If the calling semaphore does not need to block, no kernel call is made.

It is a very significant technique to manage concurrent processes by using a simple integer value, which is known as a semaphore. Semaphore is simply an integer variable that is shared between threads. This variable is used to solve the critical section problem and to achieve process synchronization in the multiprocessing environment.
Semaphores are of two types:

  1. Binary Semaphore –
    This is also known as mutex lock. It can have only two values – 0 and 1. Its value is initialized to 1. It is used to implement the solution of critical section problems with multiple processes.
  2. Counting Semaphore –
    Its value can range over an unrestricted domain. It is used to control access to a resource that has multiple instances.

Without semaphore in dispatch queue-

//
//  ViewController.swift
//  Semaphore
//
//  Created by JOYNAL ABEDIN on 3/8/22.
//

import UIKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        let queue = DispatchQueue.global()
        
        queue.async {
            let url = self.imageDownload(url: "Nature")
            print("Downloaded images successfully: \(url)")
        }
        queue.async {
            self.saveImageDirectory()
        }
    }
    
    private func imageDownload(url: String) -> String {
        sleep(4)
        return url
    }
    
    private func saveImageDirectory(){
        sleep(2)
        print("✅image save successfully!")
    }
    
}

Output:

✅image save successfully!

Downloaded images successfully: Nature

Above this output , we was see that image saved before it downloaded. if this sceneario happend any time then app will be crash.

But after using semaphore this scenerio will be –

//
//  ViewController.swift
//  Semaphore
//
//  Created by JOYNAL ABEDIN on 3/8/22.
//

import UIKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        let semaphore = DispatchSemaphore(value: 1)
        let queue = DispatchQueue.global()
        
        queue.async {
            print("✅Before execution wait first block")
            semaphore.wait() // value 0
            print("✅After execution wait first block")
            let url = self.imageDownload(url: "Nature")
            print("Downloaded images successfully: \(url)")
            semaphore.signal() // value 1
        }
        queue.async {
            print("✅Before execution wait second block")
            semaphore.wait() // value 0
            print("✅After execution wait second block")
            self.saveImageDirectory()
            semaphore.signal() // value 1
        }
    }
    
    private func imageDownload(url: String) -> String {
        sleep(4)
        return url
    }
    
    private func saveImageDirectory(){
        sleep(2)
        print("✅image save successfully!")
    }
    
}

Output:

✅Before execution wait first block

✅After execution wait first block

✅Before execution wait second block

Downloaded images successfully: Nature

✅After execution wait second block

✅image save successfully!

 

You increment a semaphore count by calling the signal() method, and decrement a semaphore count by calling wait() or one of its variants that specifies a timeout.

  • Call wait() each time before using the shared resource. We are basically asking the semaphore if the shared resource is available or not. If not, we will wait.
  • Call signal() each time after using the shared resource. We are basically signaling the semaphore that we are done interacting with the shared resource.
  • Decrement semaphore counter by 1.
  • If the resulting value is less than zero, the thread is frozen.
  • If the resulting value is equal to or bigger than zero, the code will get executed without waiting.
  • Increment semaphore counter by 1.
  • If the previous value was less than zero, this function wakes the oldest thread currently waiting in the thread queue.
  • If the previous value is equal to or bigger than zero, it means the thread queue is empty, aka, no one is waiting.

Advantages of Semaphores

Some of the advantages of semaphores are as follows −

  • Semaphores allow only one process into the critical section. They follow the mutual exclusion principle strictly and are much more efficient than some other methods of synchronization.
  • There is no resource wastage because of busy waiting in semaphores as processor time is not wasted unnecessarily to check if a condition is fulfilled to allow a process to access the critical section.
  • Semaphores are implemented in the machine independent code of the microkernel. So they are machine independent.

Disadvantages of Semaphores

Some of the disadvantages of semaphores are as follows −

  • Semaphores are complicated so the wait and signal operations must be implemented in the correct order to prevent deadlocks.
  • Semaphores are impractical for last scale use as their use leads to loss of modularity. This happens because the wait and signal operations prevent the creation of a structured layout for the system.
  • Semaphores may lead to a priority inversion where low priority processes may access the critical section first and high priority processes later.

Tips

  • ❌NEVER run semaphore wait() function on the main thread as it will freeze your app.
  • Wait() function allows us to specify a timeout. Once timeout is reached, the wait will finish regardless of semaphore count value.

491 thoughts on “DispatchSemaphore

Leave a Reply

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