线程安全隐患解决方案
解决方案 使用线程同步技术(按照预定的先后次序进行)
#import "<libkern/OSAtomic.h>"
OSSpinLock _lock = OS_SPINLOCK_INIT;
OSSpinLockLock(&_lock);
OSSpinLockUnlock(&_lock);
OSSpinLockTry(&_lock); //尝试加锁 如果不能加锁 返回No
复制代码
由于锁是自旋锁,线程不会休眠,所以当低优先级线程先对操作进行Lock造作后,CPU调度高优先级线程造作,由于低优先级别UnLock就调用高优先级线程。高优先级无法处理该操作,而高优先级线程一直调用CPU资源, 系统等待高优先级线程执行完毕后才给低优先级线程资源。
高优先级线程等待低优先级线程Unlock。低优先级线程等待CPU资源调度。造成类似死锁 导致锁无法释放。相互等待
import <os.lock.h>
os_unfair_lock _lock = OS_UNFAIR_LOCK_INIT;
os_unfair_lock_lock(&_lock);
os_unfair_lock_unlock(&_lock);
os_unfair_lock_trylock(&_lock);//尝试加锁 如果不能加锁 返回No
复制代码
#import"pthread.h"
pthread_mutexattr_t attr = {0};
pthread_mutexattr_init(&attr);
//设置锁的描述 PTHREAD_MUTEX_DEFAULT为一般互斥锁 PTHREAD_MUTEX_RECURSIVE为递归锁
//pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE为递归锁); //递归锁
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
pthread_mutex_init(mutex, &attr);
//释放 防止内存泄漏
pthread_mutexattr_destroy(&attr);
复制代码
根据参数 attr PTHREAD_MUTEX_RECURSIVE 递归锁
PTHREAD_MUTEX_RECURSIVE :允许同一个线程 重复加锁。
线程激活信号 不一定会马上执行 先等上一次操作unlock
// 创建锁
- (void) __initLock:(pthread_mutex_t *) mutex {
pthread_mutexattr_t attr = {0};
pthread_mutexattr_init(&attr);
//互斥锁
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
pthread_mutex_init(mutex, &attr);
pthread_mutexattr_destroy(&attr);
pthread_condattr_t condAtt={0};
// 创建条件
pthread_cond_init(&_condition, &condAtt);
pthread_condattr_destroy(&condAtt);
}
- (instancetype)init
{
self = [super init];
if (self) {
[self __initLock:&_lock];
dispatch_queue_t queue = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
[self test2];
});
dispatch_async(queue, ^{
sleep(2);
[self test1];
});
}
return self;
}
- (void)test1 {
NSLog(@"%@",[NSThread currentThread]);
// lock
pthread_mutex_lock(&_lock);
NSLog(@"1");
//对condition发送信号
pthread_cond_signal( &_condition);
NSLog(@"3");
//unlock
pthread_mutex_unlock(&_lock);
NSLog(@"4");
}
- (void)test2 {
NSLog(@"%@",[NSThread currentThread]);
// lock
pthread_mutex_lock(&_lock);
// 等待condition发送信号
pthread_cond_wait(&_condition, &_lock);
NSLog(@"2");
//unlock
pthread_mutex_unlock(&_lock);
}
复制代码
输出结果为
2019-09-03 14:55:49.650729+0800 GCD[1918:36391522] 1
2019-09-03 14:55:49.650896+0800 GCD[1918:36391522] 3
2019-09-03 14:55:49.651025+0800 GCD[1918:36391522] 4
2019-09-03 14:55:49.651031+0800 GCD[1918:36391523] 2
复制代码
上述代码 无论test1 和test2谁先调用 test2 依赖于test1的调用 只有当test1中执行 pthread_cond_signal( &_condition);
test2中才会继续执行, 执行步骤为:
pthread_mutex_lock(&_lock);
pthread_cond_wait(&_condition, &_lock);
此时unlock锁 并且线程休眠pthread_mutex_lock(&_lock);
pthread_cond_signal( &_condition);
test2线程被激活pthread_cond_boradcast
激活所有pthread_cond_waitNSLock是对mutex PTHREAD_MUTEX_DEFAULT
普通锁的封装
- (void)lock;// 加锁
- (void)unlock;//解锁
- (BOOL)tryLock;// 尝试是否可以加锁 如果可以 直接加锁返回YES
- (BOOL)lockBeforeDate:(NSDate *)limit; //在一定时间内是否可以加锁 如果可以 直接加锁返回YES
复制代码
NSLock是对mutex PTHREAD_MUTEX_RECURSIVE
递归锁的封装
API和NSLock一样
- (void)lock;// 加锁
- (void)unlock;//解锁
- (BOOL)tryLock;// 尝试是否可以加锁 如果可以 直接加锁返回YES
- (BOOL)lockBeforeDate:(NSDate *)limit; //在一定时间内是否可以加锁 如果可以 直接加锁返回YES
复制代码
NSCondition是对mutex和cond的封装
//用法和mutex condtion 一样 可以参考上面代码
- (void)lock;// 加锁
- (void)unlock;//解锁
- (void)wait;//等待
- (BOOL)waitUntilDate:(NSDate *)limit; //等待多久 如果超时就不等了 就直接执行
- (void)signal; // 信号量
- (void)broadcast; //广播
复制代码
NSConditionLock是对NSCondition的封装
- (void)lock;// 加锁
- (void)unlock;//解锁
- (void)lockWhenCondition:(NSInteger)condition; //休眠 直到符合conditon后 激活并lock
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition; // 解锁 修改condtion的值
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
复制代码
Dispatch_semaphore 可以控制线程的个数 当控制线程个数为1的时候 能确保同时只有1条线程去访问,已达到确保线程安全的目的
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
// 如果信号量<=0 当前线程就会键入休眠状态 知道信号量的值>0
// 如果信号值>0 就-1 然后执行下面代码
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
[super __saleTicket];
// 信号量+1
dispatch_semaphore_signal(_semaphore);
复制代码
串行队列,当在一个串行队列执行的时候,只有一个线程,能确保线程安全
以前:对mutex递归锁的封装
现在:os_unfair_recursive_lock的封装
首先打开断点汇编模式
obj4源码中objc_sync.mm
底层存储 hash表 key :传进去的对象
class recursive_mutex_tt : nocopy_t {
os_unfair_recursive_lock mLock;
...
}
using recursive_mutex_t = recursive_mutex_tt<LOCKDEBUG>;
typedef struct alignas(CacheLineSize) SyncData {
struct SyncData* nextData;
DisguisedPtr<objc_object> object;
int32_t threadCount; // number of THREADS using this block
recursive_mutex_t mutex;
} SyncData;
int objc_sync_enter(id obj)
{
int result = OBJC_SYNC_SUCCESS;
if (obj) {
SyncData* data = id2data(obj, ACQUIRE);
assert(data);
data->mutex.lock();
} else {
// @synchronized(nil) does nothing
if (DebugNilSync) {
_objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");
}
objc_sync_nil();
}
return result;
}
复制代码
int objc_sync_exit(id obj)
{
int result = OBJC_SYNC_SUCCESS;
if (obj) {
SyncData* data = id2data(obj, RELEASE);
if (!data) {
result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
} else {
bool okay = data->mutex.tryUnlock();
if (!okay) {
result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
}
}
} else {
// @synchronized(nil) does nothing
}
return result;
}
复制代码
至于耗时,测的时候每次都不太一样,大家自己测一下吧~