In the Foundation framework, NSLock mutexes, NSCondition variables, NSConditionLock conditionlocks, and NSRecursiveLock recursive locks are provided. These locks are based on the POSIX standard interface pthread_MUtex, pthread_cond, add object-oriented encapsulation. So performance is definitely not as fast as using pthread_mutex and pthread_cond directly. Now let’s understand how they work and how they work.

NSLock

NSLock is a mutex provided by the Foundation framework that uses POSIX pthread_mutex. Common apis include:

Open func 'try' () -> Bool // Keep trying to lock in the specified time, Open func lock(before LIMIT: Date) -> BoolCopy the code

It’s also very simple to use.

///swift public func test_lock() { NSLog("start") self.lock = NSLock() for i in 1... 5 { DispatchQueue.global(qos: .default).async { self.lock?.lock(before: Date().advanced(by: 100)) sleep(5) NSLog("\(i):"+Thread.current.description); self.lock? .unlock() } } NSLog("end") }Copy the code

use mutex

In the Swift Foundation source nslock. Swift file, find the following code (extracted some key code) :

private typealias _MutexPointer = UnsafeMutablePointer<pthread_mutex_t> private typealias _RecursiveMutexPointer = UnsafeMutablePointer<pthread_mutex_t> private typealias _ConditionVariablePointer = UnsafeMutablePointer<pthread_cond_t>  open class NSLock: NSObject, NSLocking { internal var mutex = _MutexPointer.allocate(capacity: 1) public override init() {// Initialize the mutex, Pthread_mutex_init (mutex, Nil)} open func lock() {pthread_mutex_lock(mutex)} open func unlock() {// Use mutex to unlock pthread_mutex_unlock(mutex) } }Copy the code

NSCondition

NSCondition is a condition variable provided by Foundation framework, and is also a mutex. The underlying is the encapsulation of POSIX pthread_cond condition variable and Pthread_mutex mutex. Pthread_mutex ensures thread-safe access to pthread_cond and lock() operations. The pthread_cond is a semaphore that tells other threads to resume or suspend the current thread. Common apis include:

Func unlock() // suspend current thread until signal open func wait() // Suspend current thread, Signal open func wait until limit: Date) -> Bool Open Func signal(); open func signal();Copy the code

It’s also very simple to use.

///swift var i = 0; public func test_condition() { NSLog("start") self.condition = NSCondition() DispatchQueue.global(qos: .default).async { self.condition?.lock() while(self.i == 0) { self.condition?.wait() } NSLog("1:"+Thread.current.description); self.condition? .unlock() } DispatchQueue.global(qos: .default).async { self.condition?.lock() while(self.i == 0) { self.condition?.wait() } NSLog("2:"+Thread.current.description); self.condition? .unlock() } DispatchQueue.global(qos: Async {self.condition?.lock() sleep(3) self.i += 1 // Wake up the thread self.condition?.signal() // self.condition?.broadcast() NSLog("3:"+Thread.current.description); self.condition? .unlock() } NSLog("end") }Copy the code

use mutex and cond

In the Swift Foundation source nslock. Swift file, find the following code (extract some key code) :

private typealias _MutexPointer = UnsafeMutablePointer<pthread_mutex_t> private typealias _ConditionVariablePointer = UnsafeMutablePointer<pthread_cond_t> open class NSCondition: NSObject, NSLocking { internal var mutex = _MutexPointer.allocate(capacity: 1) internal var cond = _ConditionVariablePointer.allocate(capacity: Pthread_mutex_init (mutex, nil) pthread_cond_init(cond, nil) Nil)} open func lock() {pthread_mutex_lock(mutex)} open func unlock() {// Use mutex to unlock Pthread_mutex_unlock (mutex)} open func wait() {pthread_cond_wait(mutex)} open func wait(until limit: Date) -> Bool { guard var timeout = timeSpecFrom(date: Limit) else {return false} return pthread_cond_timedWait (cond, mutex, &timeout) == 0} Pthread_cond_signal (cond)} open func broadcast() {// Broadcast conditional signal, broadcast all threads pthread_cond_broadcast(cond)}}Copy the code

NSConditionLock

NSConditionLock is a conditional lock provided by the Foundation framework that ensures that threads can only acquire the lock if certain conditions are met. NSConditionLock encapsulates the NSCondition variable with a cond condition of type Int. Compared with NSCondition, the degree of freedom is higher. Let’s start with common apis:

Func unlock() // unlock func unlock() // unlock func unlock() Open func 'try' () -> Bool open func tryLock(whenCondition condition: Int) -> Bool open func unlock(withCondition condition: Open func lock(before limit: Date) -> Bool // Attempts to acquire the lock before the specified time. This method blocks thread execution until the lock can be acquired or the limit is reached. open func lock(whenCondition condition: Int, before limit: Date) -> BoolCopy the code

It’s easy to use.

//swift
public func test_conditionlock(a) {
    NSLog("start")
    // Initialize the lock with cond = 3
    self.conditionLock = NSConditionLock(condition: 3)
    DispatchQueue.global(qos: .default).async {
        // Get lock and wait for cond == 1
        self.conditionLock?.lock(whenCondition: 1)
        NSLog("1:"+Thread.current.description);
        self.conditionLock?.unlock(withCondition: 2)}DispatchQueue.global(qos: .default).async {
        // Get the lock, wait cond == 4, wait up to 10 seconds
        self.conditionLock?.lock(whenCondition: 4, before: Date().advanced(by: 15))
        NSLog("2:"+Thread.current.description);
        self.conditionLock?.unlock(withCondition: 2)}DispatchQueue.global(qos: .default).async {
        // Get lock and wait for cond == 3
        self.conditionLock?.lock(whenCondition:3)
        sleep(5)
        NSLog("3:"+Thread.current.description);
        self.conditionLock?.unlock(withCondition: 1)}NSLog("end")}Copy the code

use NSCondition

NSConditionLock: conditionlock: conditionlock: conditionlock: conditionlock: conditionlock: conditionlock: conditionlock: conditionlock

open class NSConditionLock : NSObject.NSLocking {
    internal var _cond = NSCondition(a)internal var _value: Int
    internal var _thread: _swift_CFThreadRef?
    public init(condition: Int) {
    	 // Initialize the condition variable
        _value = condition
    }
    open func lock(a) {
    	 / / lock
        let _ = lock(before: Date.distantFuture)
    }
    open func unlock(a) {
        / / unlock
        _cond.lock()
        _thread = nil
        _cond.broadcast()
        _cond.unlock()
    }
    open func lock(whenCondition condition: Int) {
        let _ = lock(whenCondition: condition, before: Date.distantFuture)
    }
    open func `try`(a) -> Bool {
        return lock(before: Date.distantPast)
    }
    open func tryLock(whenCondition condition: Int) -> Bool {
        return lock(whenCondition: condition, before: Date.distantPast)
    }
    open func unlock(withCondition condition: Int) {
    	 // Unlock and set the condition variable
        _cond.lock()
        _thread = nil
        _value = condition
        _cond.broadcast()
        _cond.unlock()
    }
    open func lock(before limit: Date) -> Bool {
    	 _cond.signal()
        _cond.lock()
        while _thread ! = nil {
            if !_cond.wait(until: limit) {
                _cond.unlock()
                return false
            }
        }
        _thread = pthread_self()
        _cond.unlock()
        return true
    }
    open func lock(whenCondition condition: Int.before limit: Date) -> Bool {
    	 // Lock, not the first time or condition variable does not match, wait for _cond.signal()
        _cond.lock()
        while _thread ! = nil || _value ! = condition {
            if !_cond.wait(until: limit) {
                _cond.unlock()
                return false
            }
        }
        _thread = pthread_self()
        _cond.unlock()
        return true}}Copy the code

NSRecursiveLock

NSRecursiveLock is a recursive lock provided by the Foundation framework. The effect is that a lock can be acquired multiple times by the same thread without causing a deadlock. Like synchronized(objc_sync), the underlying encapsulation of pthread_mutex is recursive. Let’s look at the API first:

Open func 'try' () -> Bool // Keep trying to lock in the specified time, Open func lock(before LIMIT: Date) -> BoolCopy the code

It’s also easy to use:

//swift
public func test_recursivelock(a) {
    NSLog("start")
    self.recursiveLock = NSRecursiveLock(a)DispatchQueue.global(qos: .default).async {
        self.recursiveLock?.lock()
        sleep(3)
        // Get the lock again
        self.recursiveLock?.lock()
        NSLog("1:"+Thread.current.description);
        self.recursiveLock?.unlock()
        self.recursiveLock?.unlock()
    }
    DispatchQueue.global(qos: .default).async {
        self.recursiveLock?.lock()
        sleep(3)
        NSLog("2:"+Thread.current.description);
        self.recursiveLock?.unlock()
    }
    NSLog("end")}Copy the code

use recursive mutex

Next, let’s look at the underlying implementation of NSRecursiveLock. In the Swift Foundation source nsLock. Swift file, find the following code (extracted some key code) :

//swift
private typealias _MutexPointer = UnsafeMutablePointer<pthread_mutex_t>
private typealias _RecursiveMutexPointer = UnsafeMutablePointer<pthread_mutex_t>
private typealias _ConditionVariablePointer = UnsafeMutablePointer<pthread_cond_t>

open class NSRecursiveLock: NSObject.NSLocking {
    // Initialize the pthread_mutex_t lock
    internal var mutex = _RecursiveMutexPointer.allocate(capacity: 1)
    private var timeoutCond = _ConditionVariablePointer.allocate(capacity: 1)
    private var timeoutMutex = _MutexPointer.allocate(capacity: 1)

    public override init(a) {
        super.init(a)// Initialize the pthread_mutexattr_t mutex attribute and set the recursive type PTHREAD_MUTEX_RECURSIVE
        var attrib = pthread_mutexattr_t()
        withUnsafeMutablePointer(to: &attrib) { attrs in
            pthread_mutexattr_init(attrs)
            pthread_mutexattr_settype(attrs, Int32(PTHREAD_MUTEX_RECURSIVE))
            pthread_mutex_init(mutex, attrs)
        }
        pthread_cond_init(timeoutCond, nil)
        pthread_mutex_init(timeoutMutex, nil)}open func lock(a) {
    	 // Use mutex to lock
        pthread_mutex_lock(mutex)
    }
    open func unlock(a) {
    	 // Unlock with mutex
        pthread_mutex_unlock(mutex)=
        // Wakeup any threads waiting in lock(before:)
        pthread_mutex_lock(timeoutMutex)
        pthread_cond_broadcast(timeoutCond)
        pthread_mutex_unlock(timeoutMutex)
    }
    open func `try`(a) -> Bool {
        return pthread_mutex_trylock(mutex) = = 0
    }
    open func lock(before limit: Date) -> Bool {
        if pthread_mutex_trylock(mutex) = = 0 {
            return true
        }
        return timedLock(mutex: mutex, endTime: limit, using: timeoutCond, with: timeoutMutex)
        guard var endTime = timeSpecFrom(date: limit) else {
            return false
        }
        return pthread_mutex_timedlock(mutex, &endTime) = = 0}}Copy the code

The Next

  1. Common locks in iOS
  2. iOS OSSpinLock
  3. Underlying analysis of iOS Synchronized
  4. IOS NSLock underlying analysis
  5. IOS Atomic low-level analysis

Refer to the link

swift foundation opensource objc4