线程安全:多线程操作共享数据,不会造成意想不到的问题
锁的意义:为了线程安全
锁的分类
- 自旋锁 busy-waiting(忙等状态):线程获取锁,发现锁被占用,不断进行锁请求,直到获取。等待锁的线程处于忙等状态线程并会占用cpu资源;
- 互斥锁 sleep-waiting(休眠状态):线程获取锁,发现锁被占用,就向系统申请锁空闲时唤醒他并立刻休眠。等待锁的线程处于休眠状态,并不会占用cpu资源;互斥锁分为递归(可递归调用)、非递归;
- 特殊锁读写锁;
原子属性Atomic
原子属性只对setter 或getter线程安全,在获取、赋值时添加了spinlock_t锁(os_unfair_lock)
setter底层实现
if (!atomic) {
oldValue = *slot;
*slot = newValue;
} else {
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
getter底层实现
// Retain release world
id *slot = (id*) ((char*)self + offset);
if (!atomic) return *slot;
// Atomic retain release world
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
id value = objc_retain(*slot);
slotlock.unlock();
八大锁底层原理
- OSSpinLock -- 自旋锁 iOS10及以后弃用
- os_unfair_lock -- 互斥锁 iOS10开始替代OSSpinLock
- NSLock -- 互斥锁
- NSCondition -- 互斥锁
- NSConditionLock -- 互斥锁
- dispatch_semaphore -- 互斥锁
- pthread_mutex -- 互斥锁(可设置:递归)
- NSRecursiveLock -- 递归锁
-
@synchronized -- 递归锁
OSSpinLock -- 自旋锁
- 优点:不会引起调度者的睡眠,不会进行线程的调度以及CPU时间片的轮转等耗时操作;
- 缺点:在很短来时间 未获得锁的情况下,会一直占用cpu资源,会自旋死循环中,不能实现递归调用。
- 优先级反转 -- 造成死锁在iOS10被移除
- eg:如果一个低优先级的线程获得锁并访问共享资源,这时一个高优先级的线程也尝试获得这个锁,它会处于 spin lock 的忙等状态从而占用大量 CPU。此时低优先级线程无法与高优先级线程争夺 CPU 时间,从而导致任务迟迟完不成、无法释放 lock。导致陷入死锁。
系统维护了 5 个不同的线程优先级/QoS: background,utility,default,user-initiated,user-interactive。高优先级线程始终会在低优先级线程前执行,一个线程不会受到比它更低优先级线程的干扰。这种线程调度算法会产生潜在的优先级反转问题,从而破坏了 spin lock。而互斥锁不会受此影响
4.api使用
- OS_SPINLOCK_INIT 初始化锁 。
- OSSpinLockLock(&spinlock) 加锁,参数为OSSPINLOCK地址。
- OSSpinLockUnlock(&spinlock) 解锁,参数是OSSpinLock地址。
- OSSpinLockTry(&spinlock) 尝试上锁,参数是OSSpinLock地址。如果返回false,表示上锁失败,锁正在被其他线程持有。如果返回true,表示上锁成功。
os_unfair_lock -- 互斥锁
- iOS10之后开始支持,用于取代OSSpinLock。
- OS_UNFAIR_LOCK_INIT 初始化锁。
- os_unfair_lock_lock 加锁。参数为os_unfair_lock地址。
- os_unfair_lock_unlock 解锁。参数为os_unfair_lock地址。
- os_unfair_lock_trylock 尝试加锁。参数为os_unfair_lock地址。如果成功返回true。如果锁已经被锁定则返回false。
- os_unfair_lock_assert_owner 参数为os_unfair_lock地址。如果当前线程未持有指定的锁或者锁已经被解锁,则触发崩溃。
- os_unfair_lock_assert_not_owner 参数为os_unfair_lock地址。如果当前线程持有指定的锁,则触发崩溃。
//代码实现
#pragma mark - 相关的api的参数获取锁的地址
-(void)un_fair_lockTest{
self.unfarilock = OS_UNFAIR_LOCK_INIT;
for (int i = 0; i < 10; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
os_unfair_lock_lock(&self->_unfarilock);
self.count --;
NSLog(@"%d", self.count);
os_unfair_lock_unlock(&self->_unfarilock);
});
}
}
NSLock -- 互斥锁
# Swift的Foundation源码实现,NSLock基于pthread_mutex的实现
open class NSLock: NSObject, NSLocking {
internal var mutex = _MutexPointer.allocate(capacity: 1)
#if os(macOS) || os(iOS) || os(Windows)
private var timeoutCond = _ConditionVariablePointer.allocate(capacity: 1)
private var timeoutMutex = _MutexPointer.allocate(capacity: 1)
#endif
public override init() {
#if os(Windows)
InitializeSRWLock(mutex)
InitializeConditionVariable(timeoutCond)
InitializeSRWLock(timeoutMutex)
#else
pthread_mutex_init(mutex, nil)
#if os(macOS) || os(iOS)
pthread_cond_init(timeoutCond, nil)
pthread_mutex_init(timeoutMutex, nil)
#endif
#endif
}
}
- -(void)lock 加锁。
- -(void)unlock 解锁。
- -(BOOL)tryLock 尝试加锁。成功返回YES,失败返回NO。
- -(BOOL)lockBeforeDete:(NSDate *)limit 在指定时间点之前获取锁,能够获取返回YES,获取不到返回NO。
- @property (nullable ,copy) NSString *name 锁名称。
//代码实现
-(void)nslockTest{
self.nsLock = [[NSLock alloc] init];
for (int i = 0; i < 10; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self.nsLock lock];
self.count --;
NSLog(@"%d", self.count);
[self.nsLock unlock];
});
}
}
NSCondition -- 互斥锁 生产 -- 消费
与NSLock同样遵守NSLocking的协议,有同样的lock、unlock的api。
- -(void)lock 加锁。
- -(void)unlock 解锁。
- -(void)wait 阻塞当前线程,使线程进入休眠,等待唤醒信号。调用前必须已加锁。
- -(void)waitUntilDate 阻塞当前线程,使线程进入休眠,等待唤醒信号或者超时。调用前必须已加锁。
- -(void)signal 唤醒一个正在休眠的线程,如果要唤醒多个,需要调用多次。如果没有线程在等待,则什么也不做。调用前必须已加锁。存在虚假唤醒,即与broadcast的效果
- -(void)broadcast 唤醒所有在等待的线程。如果没有线程在等待,则什么也不做。调用前必须已加锁。
- @property (nullable ,copy) NSString *name 锁名称。
//代码实现
#pragma mark - 经典事例 -- 生产消费 signal(可能出现虚假唤醒)
self.nsCondition = [[NSCondition alloc] init];
-(void)nsconditionTest{
self.count = 50;
for (int i = 0; i < 50; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self production];
});
}
for (int i = 0; i < 100; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self consumption];
});
}
}
///生产
-(void)production{
[self.nsCondition lock];
self.count ++;
NSLog(@"生产:%d", self.count);
if (self.count == 1) {
//实际上会出现虚假等待,出现broadcast的效果
[self.nsCondition signal];
}
[self.nsCondition unlock];
}
///消费
-(void)consumption{
[self.nsCondition lock];
while (self.count == 0) {
[self.nsCondition wait];
}
self.count --;
NSLog(@"消费:%d", self.count);
[self.nsCondition unlock];
}
NSConditionLock -- 互斥锁是基于NSCondition进一步封装,自带条件。设置线程执行顺序
- -(void)lock 加锁。
- -(void)unlock 解锁。
- -(instancetype)initWithCondition:(NSinteger)初始化一个NSConditionLock对象。带条件(标识)
- @property(readonly) NSInteger condition 锁的条件。
- -(void)lockWhenCondition:(NSInteger)conditio满足条件时加锁。
- -(BOOL)tryLock尝试加锁。
- -(BOOL)tryLockWhenCondition如果接受对象的condition与给定的condition相等,则尝试获取锁,不足塞线程。
- -(void)unlockWithCondition:(NSInteger)condition解锁,重置锁的条件。
- -(BOOL)lockBeforDate:(NSDate *)limit在指定时间点之前获取锁。
- -(BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit在指定的时间前获取锁。
- @property (nullable ,copy) NSString *name 锁名称。
//代码实现 线程执行顺序
- (void)nsconditionLockTest {
//condition: 3 一个标识
self.conditionLock = [[NSConditionLock alloc] initWithCondition:3];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self.conditionLock lockWhenCondition:3];
NSLog(@"%@ 1", [NSThread currentThread]);
[self.conditionLock unlockWithCondition:5];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self.conditionLock lockWhenCondition:5];
NSLog(@"%@ 2", [NSThread currentThread]);
[self.conditionLock unlockWithCondition:2];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self.conditionLock lockWhenCondition:2];
NSLog(@"%@ 3", [NSThread currentThread]);
[self.conditionLock unlockWithCondition:0];
});
}
pthread_mutex -- 互斥锁
c语言实现
//代码实现
#pragma mark -- pthread_mutex
- (void)lg_pthread_mutex {
//非递归
pthread_mutex_t lock0;
pthread_mutex_init(&lock0, NULL);
pthread_mutex_lock(&lock0);
pthread_mutex_unlock(&lock0);
pthread_mutex_destroy(&lock0);
//递归
pthread_mutex_t lock;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&lock, &attr);
pthread_mutexattr_destroy(&attr);
pthread_mutex_lock(&lock);
pthread_mutex_unlock(&lock);
pthread_mutex_destroy(&lock);
}
dispatch_semaphore 控制线程并发数
dispatch_semaphore_create(intptr_t value)创建信号量,并且创建的时候需要指定信号量的大小
dispatch_semaphore_wait(dispatch_semaphore_t dsema, diapatch_time_t timeout) 等待信号量,如果信号量值为0,那么该函数就会一直等待(相当于阻塞当前线程),直到该函数等待的信号量的值大于等于1,该函数会对信号量的值进行减1操作,然后返回。dispatch_semaphore_signal(dispatch_semaphore_t dsema) 发送信号量,该函数会对信号量的值进行加1操作
dispatch_semaphore_signal 底层实现
自加1
dispatch_semaphore_signal(dispatch_semaphore_t dsema) { long value = os_atomic_inc2o(dsema, dsema_value, release);//自加1 if (likely(value > 0)) { return 0; } if (unlikely(value == LONG_MIN)) { DISPATCH_CLIENT_CRASH(value, "Unbalanced call to dispatch_semaphore_signal()"); } return _dispatch_semaphore_signal_slow(dsema); }
- dispatch_semaphore_wait 底层实现
自减1
执行顺序
dispatch_semaphore_wait ~> _dispatch_semaphore_wait_slow ~>_dispatch_sema4_wait(do-while阻塞线程,很像自旋锁)dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout) { long value = os_atomic_dec2o(dsema, dsema_value, acquire); //自减1, if (likely(value >= 0)) { return 0; } return _dispatch_semaphore_wait_slow(dsema, timeout); //_dispatch_semaphore_wait_slow 部分实现 //case DISPATCH_TIME_FOREVER:_dispatch_sema4_wait(&dsema-dsema_sema); } } _dispatch_sema4_wait(_dispatch_sema4_t *sema) { kern_return_t kr; do { kr = semaphore_wait(*sema); } while (kr == KERN_ABORTED); DISPATCH_SEMAPHORE_VERIFY_KR(kr);//有两个方法一个ret 另一个kr }
- _dispatch_semaphore_dispose 注意 wait方法数量不能多于signal方法,一般开发使用中wait 和 signal成对存在
_dispatch_semaphore_dispose(dispatch_object_t dou,
DISPATCH_UNUSED bool *allow_free)
{
dispatch_semaphore_t dsema = dou._dsema;
"//[注意]: create 不能小于0,且释放时当前发value不能小于create的值
//(即:wait方法数量不能多于signal方法)"
if (dsema->dsema_value < dsema->dsema_orig) {
DISPATCH_CLIENT_CRASH(dsema->dsema_orig - dsema->dsema_value,
"Semaphore object deallocated while in use");
}
_dispatch_sema4_dispose(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
}
///代码实现
- (void)semaphoreTest {
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%@ 1", [NSThread currentThread]);
dispatch_semaphore_signal(sem);//+1
});
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%@ 2", [NSThread currentThread]);
dispatch_semaphore_signal(sem);//+1
});
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%@ 3", [NSThread currentThread]);
dispatch_semaphore_signal(sem);//+1
});
//换行
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"\n");
});
}
NSRecursiveLock -- 递归锁 保证同一线程下重复加锁;在多线程环境下,递归调用会造成死锁,多线程在加锁和解锁中,会出现互相等待解锁的情况。与NSLock一样都是基于pthread_mutex_init实现,只是设置type为递归类型。
/*
* Mutex type attributes
*/
public var PTHREAD_MUTEX_NORMAL: Int32 { get }
public var PTHREAD_MUTEX_ERRORCHECK: Int32 { get }
public var PTHREAD_MUTEX_RECURSIVE: Int32 { get }
public var PTHREAD_MUTEX_DEFAULT: Int32 { get }
public override init() {
super.init()
#if os(Windows)
InitializeCriticalSection(mutex)
InitializeConditionVariable(timeoutCond)
InitializeSRWLock(timeoutMutex)
#else
#if CYGWIN
var attrib : pthread_mutexattr_t? = nil
#else
var attrib = pthread_mutexattr_t()
#endif
withUnsafeMutablePointer(to: &attrib) { attrs in
pthread_mutexattr_init(attrs)
pthread_mutexattr_settype(attrs, Int32(PTHREAD_MUTEX_RECURSIVE))//递归类型
pthread_mutex_init(mutex, attrs)
}
#if os(macOS) || os(iOS)
pthread_cond_init(timeoutCond, nil)
pthread_mutex_init(timeoutMutex, nil)
#endif
#endif
}
//代码实现
-(void)test{
for (int i = 0; i < 10; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self recursiveLockTest];
// [self recursiveBlockTest]
});
}
}
//
-(void)recursiveLockTest {
[self.recLock lock];
[self.recLock lock];
self.count--;
NSLog(@"%@ %d", [NSThread currentThread], self.count);
[self.recLock unlock];
[self.recLock unlock];
}
///
- (void)recursiveBlockTest {
static void(^recursiveBlock)(int);
recursiveBlock = ^(int value){
if (value > 0) {
// [self.recursive lock];
// value --;
// NSLog(@"%@ %d", [NSThread currentThread], value);
// recursiveBlock(value);
// [self.recursive unlock];
@synchronized (self) {
value --;
NSLog(@"%@ %d", [NSThread currentThread], value);
recursiveBlock(value);
}
}
};
recursiveBlock(5);
}
@synchronized -- 递归锁
加锁时,在缓存获取,不会重复创建。可以在多线程下递归调用。如性能方面要求不是非常高的话,使用该锁还更简便。
读写锁 -- 栅栏函数
读方法中的同步sync是为了栅栏函数读写互斥。读写互斥,多读单写
#pragma mark -- [注意]: lockQueue保证栅栏函数读写互斥
self.iQueue = dispatch_queue_create(@"myqueue",DISPATCH_QUEUE_CONCURRENT);
- (void)myWrite: (NSString *)name {
// 写操作
dispatch_barrier_async(self.lockQueue, ^{
[self.dataDic setObject:name forKey:@"myTest"];
});
}
- (NSString *)myRead {
__block NSString *ret;
dispatch_sync(self.lockQueue, ^{
// 读取的代码
ret = self.dataDic[@"myTest"];
});
NSLog(@"%@",ret);
return ret;
}
[注意] 死锁:当前线程在串行队列中,gcd同步(dispatch_sync)同一串行队列,导致线程死锁。
- GCD的源码 自行下载
- Swift的Fundation源码 自行下载