iOS最全的锁

·  阅读 87

多线程加锁:

自旋锁是忙等,不休眠一直等待,互斥锁是休眠等待

OSSpinLock:自旋锁, 等待锁的线程会一直处于忙等状态,一直占用着CPU资源

初始化: self.lock = OS_SPINLOCK_INIT

加锁:OSSpinLockLock(&_lock) , 解锁OSSpinLockUnlock(&_lock)

问题:会出现优先级反转的问题,优先级低的先进来加了锁, 优先级高的线程一直在忙等,CPU可能会分配大量资源给优先级高的线程,但是线程低的就得不到CPU的资源来解锁了,所以就会造成死锁

\

os_unfair_lock:iOS10以后用于取代OSSpinLock,从底层看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等,相当于互斥锁

初始化:os_unfair_lock lock = OS_UNFAIR_LOCK_INIT

尝试枷锁: os_unfair_lock_trylock(&lock)

加锁: os_unfair_lock_lock(&lock)

解锁:os_unfair_lock_unlock(&lock) 

\

pthread_mutex: 互斥锁 等待锁的线程会处于休眠状态

初始化锁:

pthread_mutexattr_t attr;

pthread_mutexattr_init(&attr);

pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);//PTHREAD_MUTEX_NORMAL 换为PTHREAD_MUTEX_RECURSIVE就变递归锁

Pthread_mutex_t mutex;

pthread_mutex_init(&mutex, &attr);

尝试加锁:pthread_mutex_try_lock(&mutex);

加锁: pthread_mutex_lock(&mutex);

解锁:pthread_mutex_unlock(&mutex);

销毁锁:pthread_mutex_destory(&attr);pthread_mutex_destory(&mutex); 使用完记得销毁

\

NSLock:就是对pthread_mutex的封装,也是互斥锁

初始化:NSLock lock = [[NSLock alloc] init];

加锁: [lock lock];

解锁: [lock unlock];

NSRecursiveLock:递归锁,可以在同一个线程重复加锁。

NSCondition: 条件锁,加锁和解锁过程中,可以设置条件,然后符合条件的地方就会解锁,然后休眠在当前地方,等待唤醒,等其他线程加锁和解锁完,可以发送信号或者广告,刚才等待的地方,就会收到信号,被唤醒然后继续执行任务并加锁和解锁。

NSConditionLock: 是NSCondition的封装,可以设置条件的值

dispatch_queue(DISPATCH_QUEUE_SERIAl):使用GCD的串行队列,也能实现线程同步

dispatch_semaphore: 信号量,可以设置初始值,控制线程并发访问的最大数量

创建信号: self.semaphore = dispatch_semaphore_create(5);同时可以有多少个线程进来访问

//如果信号量的值>0, 就会让信号量的值-1,然后往下 执行代码。当信号量的值<0的时候,就会休眠等待(内部一直在做do-while循环),直到信号量的值>=0的时候,然后继续往下执行

dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER)

//让信号量的值+1,内部修改值的时候, 会先加锁 dispatch_semaphore_signal(self.semaphore)

@synchronized:其实是堆mutex递归锁的封装,是最简单的方案

@synchronized(self){//括号里面传入不一样的对象,就代表不一样的锁

//执行需要加锁的代码

}

性能从高到底排列:

os_unfair_lock, OSSpinLock, dispath_semaphore, pthread_mutex, dispatch_queue(DISPATCH_QUEUE_SERIAL),NSCondition,@synchronized

IO操作读写安全的场景要求:多读,单写。pthread_rwlock读写锁、dispatch_barrier_async 异步栅栏调用。
初始化锁pthread_rwlock_init 、pthread_rwlock_rdlock读-加锁、pthread_rwlock_tryrdlock读-尝试加锁、pthread_rwlock_wrlock写-加锁、pthread_rwlock_trywrlock写-尝试加锁、pthread_rwlock_unlock解锁、pthread_rwlock_destroy销毁。

dispatch_barrier_async中:
读任务 dispatch_async(queue,^{}) ,
写任务 dispatch_barrier_async(queue, ^{}) 执行barrier线程任务的时候,不会执行其他线程的任务,也就是说是线程同步的。
dispatch_barrier_async 传入的必须是自己创建的并发队列(dispatch_queue_create)。
如果传入的是一个串行队列或全局并发队列,等价于dispatch_async。因为全局队列不仅有你的任务,还有其它系统的任务。如果加barrier不仅影响你自己的任务还会影响系统的任务。对于全局队列而言栅栏函数就是个普通的异步函数。
底层实现: dispatch_barrier_sync: 相当于同步执行

  • 这里进行了递归调用,循环条件是dq->do_targetq也就是仅对当前队列有效。
  • 唤醒执行栅栏前任务执行_dispatch_lane_wakeup逻辑。
  • 当栅栏前的任务执行完毕走_dispatch_lane_non_barrier_complete逻辑。这也就是为什么栅栏起作用的原因。

otomic, nonatomic, atomic只能保证属性的get和set方法是线程同步的,不能保证属性的是用过程中是线程同步的。

比如 @property(strong, atomic) NSMutableArray array;

线程安全的是; self.array = [NSMutableArray array]; NSMutableArray = self.array;

不能保证是用过程是安全的, 比如[self.array addObject:@1].

atomic太耗性能,每一个用到set和get方法的时候都需要加锁解锁,我们完全可以用nonatomic, 然后自己在需要加锁的地方加锁,也能实现现线程安全。

拷贝:

对内容拷贝一份副本

修改副本的内容,不会影响到原内容

修改原内容,不会影响到副本的内容

分为深、浅拷贝: 

浅拷贝:指针拷贝,相当于retain,仅仅引用计数+1, 不会产生新的对象

深拷贝:内容拷贝,会产生新的对象

\

不可变类型(NSString, NSArray, NSDictionary)执行copy操作是浅拷贝,因为对象本身是不可变的,执行完copy方法以后返回的也是不可变对象,所以两个对象都指向同一个不可变对象,也能达到原对象和副本互不影响,而且节省内存

不可变类型(NSString, NSArray, NSDictionary)执行mutableCopy操作是深拷贝,因为执行完mutableCopy方法以后返回的也是可变对象, 可变意味着可以修改里面的内容,而且不能影响原对象的内容,肯定在新开辟一块内存才行

\

可变类型(NSMutableString, NSMutableArray, NSMutableDictionary)执行copy操作是深拷贝,因为对象本身是可变的,执行完copy方法以后返回的虽然是不可变对象,但是修改原对象的内容的时候,不能影响新产生的对象,肯定也是内容拷贝了一份新的才行

\

可变类型(NSMutableString, NSMutableArray, NSMutableDictionary)执行mutableCopy操作是深拷贝,执行完copy方法以后返回的是可变对象,两个不可变对象,互相修改互不影响,肯定是两块不一样的内存

\

总结

copy(返回值类型)mutableCopy(返回值类型)
NSString浅(NSString)深(NSMutableString)
NSArray浅(NSArray)深(NSMutableArray)
NSDictionary浅(NSDictionary)深()
NSMutableString深(NSString)深(NSMutableString)
NSMutableArray深(NSArray)深(NSMutableArray)
NSMutableDictionary深(NSDictionary)深(NSMutableDictionary)

\

\

分类:
iOS
标签: