线程锁

作者: 痴人会说梦 | 来源:发表于2019-02-26 18:01 被阅读1次
  • 谈下iOS开发中知道的哪些锁? 哪个性能最差?SD和AFN使用的哪个? ⼀般开发中你最常用哪个? 哪个锁apple存在问题又是什么问题?

使用锁注意

  • 成对使用
  • 不能递归的锁,避免嵌套使用,造成死锁

互斥与自旋

NSLock是基于 POSIX threads 实现的,而 POSIX threads 中使用互斥量同步线程。
互斥量(或称为互斥锁)是 pthread 库为解决这个问题提供的一个基本的机制。互斥量是一个锁,它保证如下三件事情:

原子性 - 锁住一个互斥量是一个原子操作,表明操作系统保证如果在你已经锁了一个互斥量,那么在同一时刻就不会有其他线程能够锁住这个互斥量;

奇异性 - 如果一个线程锁住了一个互斥量,那么可以保证的是在该线程释放这个锁之前没有其他线程可以锁住这个互斥量;

非忙等待 - 如果一个线程(线程1)尝试去锁住一个由线程2锁住的锁,线程1会挂起(suspend)并且不会消耗任何CPU资源,直到线程2释放了这个锁。这时,线程1会唤醒并继续执行,锁住这个互斥量。

OSSpinLock

OSSpinLock叫做”自旋锁”,等待锁的线程会处于忙等(busy-wait)状态,一直占用着CPU资源

一般的锁如果第一次获得到了,后续线程再想获得就获取不到,它会释放当前所持有的资源,然后对自己进行一个阻塞行为,而自旋锁
会循环等待询问,并不释放资源. 类似于一个while循环 ,一直在访问能否获取当前锁, 如果不能获得它就继续轮询, 直到有一次能获得锁.

用于轻量级访问,简单的int值+1/-1操作
目前已经不再安全,可能会出现优先级反转问题
如果等待锁的线程优先级较高,它会一直占用着CPU资源,优先级低的线程就无法释放锁
需要导入头文件#import <libkern/OSAtomic.h>

//初始化
OSSpinLock lock = OS_SPINLOCK_INIT;

//尝试加锁(如果需要等待就不加锁,直接返回fasle;如果不需要等待就加锁,返回true);
bool result = OSSpinLockTry(&lock);

//加锁
OSSpinLockLock(&lock);

//数据的读写操作
//.....do something
//解锁
OSSpinLockUnlock(&lock);

os_unfair_lock

os_unfair_lock用于取代不安全的OSSpinLock ,从iOS10+开始才支持
从底层调用看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等
需要导入头文件#import <os/lock.h>

//初始化
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;

//尝试加锁
bool result = os_unfair_lock_trylock(&lock);

//加锁
os_unfair_lock_lock(&lock);

//解锁
os_unfair_lock_unlock(&lock);

NSLock

NSLock是对mutex普通锁的封装

NSRecursiveLock

NSRecursiveLock也是对mutex递归锁的封装,API跟NSLock基本一致

NSCondition

NSCondition是对mutex和cond的封装

NSConditionLock

NSConditionLock是对NSCondition的进一步封装,可以设置具体的条件值

pthread_mutex

mutex叫做”互斥锁”,等待锁的线程会处于休眠状态
需要导入头文件#import <pthread.h>

dispatch_semaphore

semaphore叫做”信号量”
信号量的初始值,可以用来控制线程并发访问的最大数量
信号量的初始值为1,代表同时只允许1条线程访问资源,保证线程同步

//信号量的初始值
int value = 1;
//初始化信号量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(value);

//如果信号量的值<=0,当前线程就会进入休眠等待,(直到信号量的值>0)
//如果信号量的值>0,就减1,然后往下执行后面的代码.
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

//让信号量的值+1
dispatch_semaphore_signal(semaphore);

创建信号量大致为创建一个对应的结构体

struct semaphore{
 int value; //信号量
 List<thread>;//线程控制表
}
dispatch_semaphore_wait(...){
   S.value = S.value - 1;
   if(S.value < 0){
    //主动阻塞行为
     Block(S.list);
    }
}
dispatch_semaphore_signal(...){
   S.value = S.value + 1;
  //列表中需要有相应的线程需要唤醒
  ///唤醒是一个被动行为
  if S.value <= 0{
     wakeup(S.List);
   }
}

dispatch_queue

直接使用GCD的串行队列,也是可以实现线程同步的

dispatch_queue_t queue = dispatch_queue_create("com.serial.queue",DISPATCH_QUEUE_SERIAL);

dispatch_sync(queue,^{
   //任务 
});

@synchronized

一般在创建单例对象的时候使用,来保证在多线程环境下创建的对象是唯一的.

@synchronized是对mutex递归锁的封装
源码查看:objc4中的objc-sync.mm文件
@synchronized(obj)内部会生成obj对应的递归锁,然后进行加锁、解锁操作

@synchronized(obj){

}

atomic

  • 修饰属性的关键字
  • 对被修饰对象进行原子操作(不负责使用)

atomic用于保证属性setter、getter的原子性操作,相当于在getter和setter内部加了线程同步的锁
可以参考源码objc4的objc-accessors.mm
它并不能保证使用属性的过程是线程安全的

@property (atomic)NSMutableArray array;

self.array = [[NSMutableArray alloc]init];✅
[self.array addObject:obj];❎

NSLock

一般是用来解决细粒度的线程同步问题,来保证各个线程互斥,进入自己的临界区.
不可以递归,重入会造成死锁

[lock lock];
...操作逻辑
[lock unlock];

NSRecursiveLock

递归锁,可以重入.

性能由高到低

  • os_unfair_lock
  • OSSpinLock
  • dispatch_semaphore
  • pthread_mutex
  • dispatch_queue(DISPATCH_QUEUE_SERIAL)
  • NSLock
  • NSCondition
  • pthread_mutex(recursive)
  • NSRecursiveLock
  • NSConditionLock
  • @synchronized

什么情况使用自旋锁比较划算?
预计线程等待锁的时间很短
加锁的代码(临界区)经常被调用,但竞争情况很少发生
CPU资源不紧张
多核处理器

什么情况使用互斥锁比较划算?
预计线程等待锁的时间较长
单核处理器
临界区有IO操作
临界区代码复杂或者循环量大
临界区竞争非常激烈

问题

SD中使用的是@synchronized和信号量

#define LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
#define UNLOCK(lock) dispatch_semaphore_signal(lock);

AFN中使用的@synchronized和信号量
OSSpinLock的不安全问题

相关文章

  • 4.0.6.守护线程,线程死锁

    守护线程会随着主线程的结束而结束DaemonThread 线程 1, 线程 2,锁1,锁2 线程1 有锁1,想拿锁...

  • 悲观锁:一个线程得到锁,其它线程挂起,synchronized 乐观锁:一个线程得到锁,其它线程不断重试, cas...

  • sleep,wait, join yield

    锁池:所有需要竞争同步锁的线程都会放在锁池中,当一个线程得到锁后,其他线程都会在锁池中等待,当线程释放锁之后,其他...

  • 并发编程-线程

    线程 GIL 守护线程 线程锁(互斥锁 and 递归锁) 信号量 事件 条件 定时器 1.线程: 特点在多线程的操...

  • 深入理解AQS(二)- 共享模式

    共享锁与独占锁 独占锁被某个线程持有时,其他线程只能等待当前线程释放后才能去竞争锁,而且只有一个线程能竞争锁成功。...

  • iOS中各种锁的性能对比

    自旋锁 与 互斥锁 自旋锁 (spin lock): 如果一个线程需要获取自旋锁,该锁已经被其他线程占用,该线程不...

  • 死锁

    什么是死锁 简单的说:线程1持有A锁,线程2持有B锁;线程1尝试获取B锁,线程2尝试获取A锁。两个线程各持有了一把...

  • 公平锁和非公平锁-ReentrantLock是如何实现公平、非公

    1、什么是公平锁与非公平锁 公平锁:公平锁就是保障了多线程下各线程获取锁的顺序,先到的线程优先获取锁。非公平锁:非...

  • 2020-03-11 公平锁与非公平锁

    补: 公平锁:多个线程情况下排队,先到先获得锁 非公平锁:当锁被释放后,所有线程竞争锁,抢到的线程就会获得锁 非公...

  • LINUX线程

    创建线程 启动线程 线程锁

网友评论

    本文标题:线程锁

    本文链接:https://www.haomeiwen.com/subject/yulzyqtx.html