iOS 中实现线程安全
实现线程安全可以通过加锁、单线程访问等方式。
iOS 中常用的几种锁:
os_unfair_lock
pthread_mutex
dispatch_semaphore
NSLock
NSRecursiveLock
NSCondition
NSConditionLock
@synchorized
单线程访问:
dispatch_queue(DISPATCH_QUEUE_SERIAL),串行队列
dispatch_barrier_async
一、os_unfair_lock
#import <os/lock.h>
static os_unfair_lock _unfailLock; static int _osCount; - (void)unfailrLock { _unfailLock = OS_UNFAIR_LOCK_INIT; // 初始化锁 _osCount = 20; for (int i = 0; i < 20; i++) { dispatch_async(dispatch_get_global_queue(0, 0), ^{ [self unfailrLockBlock]; }); } } - (void)unfailrLockBlock { os_unfair_lock_lock(&_unfailLock); // 加锁 sleep(1); _osCount--; NSLog(@"os_unfair_lock %ld", (long)_osCount); os_unfair_lock_unlock(&_unfailLock); // 解锁 }
二、pthread_mutex
static pthread_mutex_t _mutexLock;
static int _mutexCount;
- (void)pthreadMutex {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT); // PTHREAD_MUTEX_RECURSIVE 递归锁
pthread_mutex_init(&_mutexLock, &attr); // pthread_mutex_init(&_mutexLock, NULL);
_mutexCount = 20;
for (int i = 0; i < 20; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self pthreadMutexBlock];
});
}
}
- (void)pthreadMutexBlock {
pthread_mutex_lock(&_mutexLock);
sleep(1);
_mutexCount--;
NSLog(@"pthread_mutex %ld", (long)_mutexCount);
pthread_mutex_unlock(&_mutexLock);
}
三、dispatch_semaphore,当信号量小于1的时候,线程进入休眠等待。
static dispatch_semaphore_t _semaphore; static int _semaphoreCount; - (void)semaphore { _semaphore = dispatch_semaphore_create(1); _semaphoreCount = 20; for (int i = 0; i < 20; i++) { dispatch_async(dispatch_get_global_queue(0, 0), ^{ [self semaphoreBlock]; }); } } - (void)semaphoreBlock { dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER); sleep(1); _semaphoreCount--; NSLog(@"semaphore %ld", (long)_semaphoreCount); dispatch_semaphore_signal(_semaphore); }
四、NSLock 对pthread_mutex 普通类型锁的封装,NSRecursiveLock pthread_mutex 递归类型锁的封装。递归锁,同一个线程可以对一个锁多次加锁。
static NSLock *_nslock; static int _lockCount; - (void)nslock { _nslock = [[NSLock alloc] init]; _lockCount = 20; for (int i = 0; i < 20; i++) { dispatch_async(dispatch_get_global_queue(0, 0), ^{ [self nslockBlock]; }); } } - (void)nslockBlock { [_nslock lock]; sleep(1); _lockCount--; NSLog(@"nslock %ld", (long)_lockCount); [_nslock unlock]; }
五、NSCondition、NSConditionLock 是对 pthread_mutex 的 condition 属性封装。
static NSCondition *_cond; static int _condCount; - (void)condition { _cond = [[NSCondition alloc] init]; _condCount = 10; for (int i = 0; i < 15; i++) { dispatch_async(dispatch_get_global_queue(0, 0), ^{ [self reduce]; }); } for (int i = 0; i < 10; i++) { dispatch_async(dispatch_get_global_queue(0, 0), ^{ [self add]; }); } } - (void)reduce { [_cond lock]; if (_condCount == 0) { [_cond wait]; } sleep(1); _condCount--; NSLog(@"reduce %ld", (long)_condCount); [_cond unlock]; } - (void)add { [_cond lock]; if (_condCount >= 0) { [_cond signal]; // 一次singnal只能唤醒一个在等待condition的线程,如果想唤醒多个可使用[_cond broadcast]; } sleep(1); _condCount++; NSLog(@"add %ld", (long)_condCount); [_cond unlock]; }
六、@synchorized,runtime 会给 synchorized 调用的每一个对象分配一个递归锁并存储在哈希表中,因此效率比较低不建议使用。
@synchorized(nil) 会加锁失败。
七、pthread_rwlock 用于文件读写操作的锁,pthread_rwlock_rdlock(&lock) 读操作加锁,临界区(被加锁的代码)并发执行,pthread_rwlock_wrlock(&lock))写操作加锁,临界区只会串行执行
static pthread_rwlock_t _rwlock = PTHREAD_RWLOCK_INITIALIZER; static int _rwlockCount; - (void)pthreadRWLock { _rwlockCount = 10; for (int i = 0; i < 15; i++) { dispatch_async(dispatch_get_global_queue(0, 0), ^{ [self rwRead]; }); } for (int i = 0; i < 2; i++) { dispatch_async(dispatch_get_global_queue(0, 0), ^{ [self rwWrite]; }); } } - (void)rwRead { pthread_rwlock_rdlock(&_rwlock); sleep(1); NSLog(@"pthread_rw read %ld", (long)_rwlockCount); pthread_rwlock_unlock(&_rwlock); } - (void)rwWrite { pthread_rwlock_wrlock(&_rwlock); sleep(1); _rwlockCount++; NSLog(@"pthread_rw write %ld", (long)_rwlockCount); pthread_rwlock_unlock(&_rwlock); } 输出 2020-06-08 22:46:01.137977+0800 多线程[7019:829191] pthread_rw read 10 2020-06-08 22:46:01.137977+0800 多线程[7019:829189] pthread_rw read 10 2020-06-08 22:46:01.137978+0800 多线程[7019:829194] pthread_rw read 10 2020-06-08 22:46:01.137978+0800 多线程[7019:829190] pthread_rw read 10 2020-06-08 22:46:01.137989+0800 多线程[7019:829195] pthread_rw read 10 2020-06-08 22:46:01.137977+0800 多线程[7019:829188] pthread_rw read 10 2020-06-08 22:46:01.138022+0800 多线程[7019:829256] pthread_rw read 10 2020-06-08 22:46:01.138021+0800 多线程[7019:829252] pthread_rw read 10 2020-06-08 22:46:01.138022+0800 多线程[7019:829257] pthread_rw read 10 2020-06-08 22:46:01.138024+0800 多线程[7019:829255] pthread_rw read 10 2020-06-08 22:46:01.138022+0800 多线程[7019:829254] pthread_rw read 10 2020-06-08 22:46:01.138028+0800 多线程[7019:829253] pthread_rw read 10 2020-06-08 22:46:01.138047+0800 多线程[7019:829258] pthread_rw read 10 2020-06-08 22:46:01.138048+0800 多线程[7019:829259] pthread_rw read 10 2020-06-08 22:46:01.138053+0800 多线程[7019:829260] pthread_rw read 10 2020-06-08 22:46:02.143706+0800 多线程[7019:829261] pthread_rw write 11 2020-06-08 22:46:03.144493+0800 多线程[7019:829262] pthread_rw write 12
七、atomic、自旋锁与互斥锁
atomic:原子性,当一个属性被标记为atomic的时候,get和set方法通过加锁的方法实现原子性,iOS手机上一般不使用atomic属性。
自旋锁:当线程获取不到锁的时候,线程处于忙等状态(类似while循环),cpu处于忙等状态。
互斥锁:当线程获取不到锁的时候,线程进入休眠状态,cpu 资源得到释放。目前 iOS 的锁基本都是互斥锁。