在Foundation
框架中,提供了NSLock
互斥锁、NSCondition
条件变量、NSConditionLock
条件锁、NSRecursiveLock
递归锁。
这些锁都是在POSIX
标准接口的pthread_mutex
、pthread_cond
基础上,增加了面向对象的封装。所以性能上肯定没有直接使用pthread_mutex
和pthread_cond
快。接下来我们来了解下他们的使用方法和原理。
NSLock
NSLock
是Foundation
框架提供的互斥锁,底层使用了POSIX
的pthread_mutex
互斥锁。常用API有:
///加锁
func lock()
///解锁
func unlock()
///尝试加锁,返回是否成功,不阻塞
open func `try`() -> Bool
///在指定时间内一直尝试加锁,返回是否成功
open func lock(before limit: Date) -> Bool
使用也非常简单。
///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")
}
use mutex
接下来我们来看看NSLock
底层实现,在Swift Foundation源码 NSLock.swift
文件中,找到以下代码(提取了部分关键代码):
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() {
//初始化互斥锁,没有递归类型的属性
pthread_mutex_init(mutex, nil)
}
open func lock() {
//使用mutex加锁
pthread_mutex_lock(mutex)
}
open func unlock() {
//使用mutex解锁
pthread_mutex_unlock(mutex)
}
}
NSCondition
NSCondition
是Foundation
框架提供的条件变量,也是一种互斥锁,底层是对POSIX
的pthread_cond
条件变量和pthread_mutex
互斥量的封装。
pthread_mutex
保证了访问pthread_cond
和lock()
操作的线程安全。pthread_cond
通过信号量的方式,通知其他线程恢复或者挂起当前线程。常用API有:
///加锁
func lock()
///解锁
func unlock()
///挂起当前线程,直到收到signal
open func wait()
///在指定时间内,挂起当前线程,直到收到signal
open func wait(until limit: Date) -> Bool
///发送条件信号,唤醒一个(优先级最高或者等待时间最长的)等待线程
open func signal()
///广播条件信号,唤醒所有等待线程
open func broadcast()
使用也非常简单。
///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: .default).async {
self.condition?.lock()
sleep(3)
self.i += 1
//唤醒线程
self.condition?.signal()
// self.condition?.broadcast()
NSLog("3:"+Thread.current.description);
self.condition?.unlock()
}
NSLog("end")
}
use mutex and cond
接下来我们来看看NSCondition
底层实现,在Swift Foundation源码 NSLock.swift
文件中,找到以下代码(提取了部分关键代码):
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: 1)
public override init() {
//初始化mutex、cond
pthread_mutex_init(mutex, nil)
pthread_cond_init(cond, nil)
}
open func lock() {
//使用mutex加锁
pthread_mutex_lock(mutex)
}
open func unlock() {
//使用mutex解锁
pthread_mutex_unlock(mutex)
}
open func wait() {
//挂起当前线程
pthread_cond_wait(cond, 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
}
open func signal() {
//发送条件信号,唤醒线程
pthread_cond_signal(cond)
}
open func broadcast() {
//广播条件信号,唤醒所有线程
pthread_cond_broadcast(cond)
}
}
NSConditionLock
NSConditionLock
是Foundation
框架提供的条件锁,可以确保线程仅在满足特定条件时才能获取锁。
NSConditionLock
其实是对NSCondition
条件变量的封装,内置了Int
类型的cond
条件。相比来说还是NSCondition
可定制自由度高一点。
我们先来看常用API:
///加锁
func lock()
///解锁
func unlock()
///获取锁,条件变量等于指定的值,才能获取锁,一直等待
open func lock(whenCondition condition: Int)
///尝试加锁,返回是否成功,不阻塞
open func `try`() -> Bool
///如果条件变量等于某个值,尝试加锁,返回是否成功
open func tryLock(whenCondition condition: Int) -> Bool
///解锁,并设置条件变量的值
open func unlock(withCondition condition: Int)
///在指定时间内一直尝试加锁,返回是否成功
open func lock(before limit: Date) -> Bool
///尝试在指定的时间之前获取锁,此方法将阻止线程的执行,直到可以获取锁或达到限制为止。
open func lock(whenCondition condition: Int, before limit: Date) -> Bool
使用也很简单。
//swift
public func test_conditionlock() {
NSLog("start")
//初始化锁,设置 cond = 3
self.conditionLock = NSConditionLock(condition: 3)
DispatchQueue.global(qos: .default).async {
//获取锁,等待 cond == 1
self.conditionLock?.lock(whenCondition: 1)
NSLog("1:"+Thread.current.description);
self.conditionLock?.unlock(withCondition: 2)
}
DispatchQueue.global(qos: .default).async {
//获取锁,等待 cond == 4,最多等待10秒
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 {
//获取锁,等待 cond == 3
self.conditionLock?.lock(whenCondition:3)
sleep(5)
NSLog("3:"+Thread.current.description);
self.conditionLock?.unlock(withCondition: 1)
}
NSLog("end")
}
use NSCondition
接下来我们来看看NSConditionLock
底层实现,在Swift Foundation源码 NSLock.swift
文件中,找到以下代码(提取了部分关键代码):
open class NSConditionLock : NSObject, NSLocking {
internal var _cond = NSCondition()
internal var _value: Int
internal var _thread: _swift_CFThreadRef?
public init(condition: Int) {
//初始化条件变量
_value = condition
}
open func lock() {
//加锁
let _ = lock(before: Date.distantFuture)
}
open func 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`() -> 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) {
//解锁,设置条件变量
_cond.lock()
_thread = nil
_value = condition
_cond.broadcast()
_cond.unlock()
}
open func lock(before limit: Date) -> Bool {
//加锁,首次直接unlock,其他case需要等待_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 {
//加锁,不是首次或者条件变量不匹配,等待_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
}
}
NSRecursiveLock
NSRecursiveLock
是Foundation
框架提供的递归锁。作用是,一个锁可以由同一线程多次获取而不会导致死锁,和synchronized(objc_sync)
一样,底层也是对pthread_mutex recursive
的封装。
我们先来看下API:
///加锁
func lock()
///解锁
func unlock()
///尝试加锁,返回是否成功,不阻塞
open func `try`() -> Bool
///在指定时间内一直尝试加锁,返回是否成功
open func lock(before limit: Date) -> Bool
使用起来也很简单:
//swift
public func test_recursivelock() {
NSLog("start")
self.recursiveLock = NSRecursiveLock()
DispatchQueue.global(qos: .default).async {
self.recursiveLock?.lock()
sleep(3)
//重复获取锁
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")
}
use recursive mutex
接下来我们来看看NSRecursiveLock
底层实现,在Swift Foundation源码 NSLock.swift
文件中,找到以下代码(提取了部分关键代码):
//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 {
//初始化 pthread_mutex_t 锁
internal var mutex = _RecursiveMutexPointer.allocate(capacity: 1)
private var timeoutCond = _ConditionVariablePointer.allocate(capacity: 1)
private var timeoutMutex = _MutexPointer.allocate(capacity: 1)
public override init() {
super.init()
//初始化 pthread_mutexattr_t 互斥属性,设置递归类型 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() {
//使用mutex加锁
pthread_mutex_lock(mutex)
}
open func unlock() {
//使用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`() -> 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
}
}