OSSpinLock
-
自旋锁
, 等待锁的线程一直处于(busy-wait)忙等
, 一直占用着CPU资源 - 目前已经不推荐使用, 会有
优先级反转的问题
- 如果等待锁的线程优先级较高, 它会一直占用着CPU资源, 优先级低的线程可能无法释放锁
self.lock = OS_SPINLOCK_INIT; //初始化
- (void)__saveMoney
{
OSSpinLockLock(&_lock);
[super __saveMoney];
OSSpinLockUnlock(&_lock);
}
假设有如下两个线程
线程A: 优先级高
线程B: 优先级低
优先级低
的线程B, 先获取到了锁, 加锁后, 执行代码, 但由于线程A的优先级比较高, 系统会一直分配CPU资源给A
, 线程B就可能一直没有足够的时间执行, 可能就会造成线程B无法解锁
, 这样就造成了优先级反转
的问题
os_unfair_lock
从汇编调用过程来看,其实是一个互斥锁,苹果文档里写的是low-level lock
- iOS10开始, 用来取代OSSpinlock, 是一种不公平的锁
刚解锁的线程可能会再次获得锁, 其他线程继续等待
- 等待os_unfair_lock锁的线程是
处于休眠
状态,并非忙等
self.lock = OS_UNFAIR_LOCK_INIT;
- (void)__saveMoney
{
os_unfair_lock_lock(&_lock);
[super __saveMoney];
os_unfair_lock_unlock(&_lock);
}
pthread_mutex_t
-
互斥锁
, Unix,Linux跨平台的锁, 等待锁的线程会处于休眠
状态
pthread_mutex_init(mutex, NULL); // 传null是default
// pthread_mutexattr_t attr;
// pthread_mutexattr_init(&attr);
// pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
// pthread_mutexattr_destroy(&attr);
pthread_mutex_lock(&_mutex);
NSLog(@"%s",__func__);
pthread_mutex_unlock(&_mutex);
pthread_mutex_cond
-
条件
可以设置pthread_mutex_cond 进行相应的条件等待
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&_mutex, &attr); // 传null是default
pthread_mutexattr_destroy(&attr);
pthread_cond_init(&_cond, 0);
- (void)__remove
{
// 假设先调用的删除,但是数组里并没有数据
pthread_mutex_lock(&_mutex);
NSLog(@"_remove -- begin");
// 这时,线程进行休眠等待状态,并释放锁, 其他线程可获得锁
if (self.data.count == 0) {
pthread_cond_wait(&_cond, &_mutex);
}
[self.data removeLastObject];
pthread_mutex_unlock(&_mutex);
}
- (void)__add
{
// add方法的线程获取到锁,进行加锁
pthread_mutex_lock(&_mutex);
NSLog(@"_add -- begin");
[self.data addObject:@"123"];
// 完成任务 发送条件锁信号,唤醒remove线程继续执行任务
pthread_cond_signal(&_cond);
pthread_mutex_unlock(&_mutex);
}
pthread_mutex_recursive
-
递归锁
, 多个线程访问,可以对同一个锁对象进行多次加锁,解锁
NSLock & NSRecursiveLock
- pthread_mutex_t
普通锁
和递归锁
的OC封装, 更加面相对象, 方便我们使用
NSCondition
- 对 pthread_mutex_t
mutex
和cond
(ˈkɒnd) 的封装
NSConditionLock
- 是对NSCondition的进一步封装, 封装了条件的具体值
- 设置
锁的条件
, 可以让子线程
根据条件
按顺序执行
任务
self.conditionLock = [[NSConditionLock alloc] initWithCondition:1];
- (void)otherTest
{
[[[NSThread alloc] initWithTarget:self selector:@selector(__one) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__two) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__three) object:nil] start];
}
- (void)__one
{
[self.conditionLock lockWhenCondition:1];
NSLog(@"__one");
sleep(1);
[self.conditionLock unlockWithCondition:2];
}
- (void)__two
{
[self.conditionLock lockWhenCondition:2];
NSLog(@"__two");
sleep(1);
[self.conditionLock unlockWithCondition:3];
}
- (void)__three
{
[self.conditionLock lockWhenCondition:3];
NSLog(@"__three");
[self.conditionLock unlockWithCondition:4];
}
2022-12-19 20:23:04.257210+0800 多线程-安全隐患[74891:9502277] __one
2022-12-19 20:23:05.262859+0800 多线程-安全隐患[74891:9502278] __two
2022-12-19 20:23:06.267912+0800 多线程-安全隐患[74891:9502279] __three
dispatch_semaphore todo 补充几个代码中的实际运用, 以及丰富一下解释
-
控制线程的最大并发数量
dispatch_semaphore_create(value)
初始化一个信号量, value为信号量的值
dispatch_semaphore_wait(信号量对象, 等待的时间)
信号量等待 当信号量的值 > 0时, 会让信号量的值- 1
, 信号量的值 <= 0时, 其他线程会进入休眠等待
dispatch_semaphore_signal(信号量对象)
发送一个信号, 信号量的值 + 1 -
控制线程的最大并发数量,同一时间最多只有5个线程进入, 5个线程执行完毕后, 在新进入5个线程
self.moneySemaphore = dispatch_semaphore_create(1);
- (void)__saveMoney
{
dispatch_semaphore_wait(_moneySemaphore, DISPATCH_TIME_FOREVER);
[super __saveMoney];
dispatch_semaphore_signal(_moneySemaphore);
}
self.semaphore = dispatch_semaphore_create(5);
- (void)otherTest
{
for (int i = 0; i < 20; i++) {
[[[NSThread alloc] initWithTarget:self selector:@selector(test) object:nil] start];
}
}
- (void)test {
dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
sleep(1);
NSLog(@"test - %@", [NSThread currentThread]);
dispatch_semaphore_signal(_semaphore);
}
拓展 多个异步请求任务
,相互依赖,请求本身属于异步
。c依赖b的结果,b依赖a的结果。如何实现?
- 如果是异步的任务, 网络请求也是异步, 这样在异步执行的时候, 只要代码执行完 就算任务结束, 但是这个时, 可能只是异步的网络请求发送出去了, 数据并未返回, 其实我们真正想要的效果是 异步请求的数据返回后 才算真正的任务结束
- (void)otherTest {
NSURLSession *session = [NSURLSession sharedSession];
dispatch_semaphore_t sem = dispatch_semaphore_create(1);
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
dispatch_async(q, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"请求A 返回数据");
dispatch_semaphore_signal(sem);
}];
[task resume];
});
dispatch_async(q, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"请求B 返回数据");
dispatch_semaphore_signal(sem);
}];
[task resume];
});
dispatch_async(q, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"请求C 返回数据");
dispatch_semaphore_signal(sem);
}];
[task resume];
dispatch_semaphore_signal(sem);
});
}
@synchroized(对象)
- 一个对象关联一把锁, 根据传入的对象的地址获取锁, 底层是hashmap[对象]获取锁, 是对 pthread_mutex_t 递归锁的封装
网友评论