iOS多线程安全-13种线程锁

本文介绍了iOS中线程安全的重要性,以及自旋锁和互斥锁的区别。详细讨论了13种线程锁,包括OSSpinLock、os_unfair_lock、pthread_mutex、NSLock、NSRecursiveLock、NSCondition、NSConditionLock、@synchronized等,并给出了性能比较,展示了如何在不同场景下选择合适的锁类型。
摘要由CSDN通过智能技术生成

目录

  • 1、为什么要线程安全
  • 2、自旋锁和互斥锁
  • 3、锁的类型* 1、OSSpinLock* 2、os_unfair_lock* 3、pthread_mutex* 4、dispatch_semaphore* 5、dispatch_queue(DISPATCH_QUEUE_SERIAL)* 6、NSLock* 7、NSRecursiveLock* 8、NSCondition* 9、NSConditionLock* 10、@synchronized* 11、pthread_rwlock* 12、dispatch_barrier_async* 13、atomic
  • 4、锁的性能比较

为什么要线程安全

多个线程访问同一块资源的时候,很容易引发数据混乱问题。 一个大家都喜欢拿来举例子的就是买票demo,今天我使用这个案例 假设有100张票,同时开5个窗口买票,5个窗口买票,我们来看看结果

//卖票演示
- (void)ticketTest{
self.ticketsCount = 50;
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

for (NSInteger i = 0; i < 5; i++) {
dispatch_async(queue, ^{
for (int i = 0; i < 10; i++) {
[self sellingTickets];
}
});
}

}
//卖票
- (void)sellingTickets{
int oldMoney = self.ticketsCount;
sleep(.2);
oldMoney -= 1;
self.ticketsCount = oldMoney;

NSLog(@"当前剩余票数-> %d", oldMoney);
} 

正常情况下我有50张票,然后卖了50次,剩余票数应该是0,但是打印结果竟然是3,所以这里就存在了线程安全问题。

出现线程安全的原因

出现线程安全的原因就是在同一个时间,多个线程同时读取一个值,像线程A和B同时读取了当前票数为10,等于是卖了两张票,但是总票数其实就减少了一张。

解决方法

使用线程同步技术,按照预定的先后次序依次进行,常见的线程同步技术就是加锁

自旋锁和互斥锁

自旋锁(Spin lock)

自旋锁与互斥锁有点类似,只是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是 否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。其作用是为了解决某项资源的互斥使用。因为自旋锁不会引起调用者睡眠,所以自旋锁的效率远 高于互斥锁。虽然它的效率比互斥锁高,但是它也有些不足之处:     1、自旋锁一直占用CPU,他在未获得锁的情况下,一直运行--自旋,所以占用着CPU,如果不能在很短的时 间内获得锁,这无疑会使CPU效率降低。     2、在用自旋锁时有可能造成死锁,当递归调用时有可能造成死锁,调用有些其他函数也可能造成死锁,如 copy_to_user()、copy_from_user()、kmalloc()等。     因此我们要慎重使用自旋锁,自旋锁只有在内核可抢占式或SMP的情况下才真正需要,在单CPU且不可抢占式的内核下,自旋锁的操作为空操作。自旋锁适用于锁使用者保持锁时间比较短的情况下。

互斥锁

互斥锁属于sleep-waiting类型的锁。例如在一个双核的机器上有两个线程(线程A和线程B),它们分别运行在Core0和 Core1上。假设线程A想要通过pthread_mutex_lock操作去得到一个临界区的锁,而此时这个锁正被线程B所持有,那么线程A就会被阻塞 (blocking),Core0 会在此时进行上下文切换(Context Switch)将线程A置于等待队列中,此时Core0就可以运行其他的任务(例如另一个线程C)而不必进行忙等待。而自旋锁则不然,它属于busy-waiting类型的锁,如果线程A是使用pthread_spin_lock操作去请求锁,那么线程A就会一直在 Core0上进行忙等待并不停的进行锁请求,直到得到这个锁为止。

两种锁的加锁原理

互斥锁:线程会从sleep(加锁)——>running(解锁),过程中有上下文的切换,cpu的抢占,信号的发送等开销。

自旋锁:线程一直是running(加锁——>解锁),死循环检测锁的标志位,机制不复杂。

对比 互斥锁的起始原始开销要高于自旋锁,但是基本是一劳永逸,临界区持锁时间的大小并不会对互斥锁的开销造成影响,而自旋锁是死循环检测,加锁全程消耗cpu,起始开销虽然低于互斥锁,但是随着持锁时间,加锁的开销是线性增长。

两种锁的应用

互斥锁用于临界区持锁时间比较长的操作,比如下面这些情况都可以考虑

  • 1 临界区有IO操作
  • 2 临界区代码复杂或者循环量大
  • 3 临界区竞争非常激烈
  • 4 单核处理器

至于自旋锁就主要用在临界区持锁时间非常短且CPU资源不紧张的情况下,自旋锁一般用于多核的服务器。

13种锁

1、OSSpinLock

OSSpinLock叫做”自旋锁”,使用时需要导入头文件#import <libkern/OSAtomic.h>

//初始化
OSSpinLock lock = OS_SPINLOCK_INIT;
//加锁
OSSpinLockLock(&lock);
//解锁
OSSpinLockUnlock(&lock); 

demo

#import "OSSpinLockDemo.h"
#import <libkern/OSAtomic.h>
@interface OSSpinLockDemo()
@property (assign, nonatomic) OSSpinLock ticketLock;
@end

@implementation OSSpinLockDemo

- (instancetype)init
{
self = [super init];
if (self) {
self.ticketLock = OS_SPINLOCK_INIT;
}
return self;
}

//卖票
- (void)sellingTickets{
OSSpinLockLock(&_ticketLock);

[super sellingTickets];

OSSpinLockUnlock(&_ticketLock);
}

@end 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值