自旋锁 OSSpinLock

特点:不是一个安全的锁, 等待锁的线程会处于忙等(busy-wait)状态, 一直占用着CPU资源; (类似一个while(1)循环一样,不停的查询锁的状态, 注意区分Runloop的机制, 同样是阻塞, 但是Runloop是类似休眠的阻塞, 不会耗费CPU资源, 自旋锁的这种忙等机制使它相比其他锁效率更高, 因为没有唤醒-休眠这些类似操作, 从而能更快去处理事情);

自旋锁同一线程重复加锁会造成死锁问题。iOS10以上废弃,不推荐使用

互斥锁 os_unfair_lock

os_unfair_lock是iOS 10及以上版本引入的一种自旋锁,也被称为不公平锁。它是取代OSSpinLock的一种更为高效、更为安全的锁机制。
os_unfair_lock采用了更复杂的底层实现,可以避免OSSpinLock可能存在的优先级反转问题。它不会忙等待,而是采用了更智能的等待策略。当锁被占用时,等待线程会进入休眠状态,不再占用CPU资源,直到锁可用时被唤醒。
使用os_unfair_lock要比OSSpinLock更加安全,也更不容易出现死锁等问题。而且,相较于传统的互斥锁pthread_mutex_t,os_unfair_lock的性能也有所提升,因为它避免了不必要的系统调用。

需要注意的是,os_unfair_lock并不是递归锁,因此在同一线程中重复加锁会导致死锁。另外,由于os_unfair_lock是iOS 10及以上版本引入的,如果要兼容旧版本的iOS系统,建议在代码中进行系统版本的判断。

总结来说,os_unfair_lock是iOS中一种高效且安全的自旋锁,推荐使用它来代替OSSpinLock和传统的互斥锁,以确保线程安全性和性能优化。

互斥/递归/条件锁 pthread_mutex_t

pthread_mutex_t是一种互斥锁(Mutex Lock),它是POSIX线程库(Portable Operating System Interface for Unix)中提供的一种线程同步机制。互斥锁用于保护共享资源,确保在同一时间只有一个线程可以访问临界区,从而避免多个线程同时对共享资源进行修改造成的数据不一致和竞争条件。

pthread_mutex_t的使用非常灵活,可以根据需要设置不同的锁属性,如锁类型、锁的递归性、锁的协议等。互斥锁有多种类型,包括:

  1. PTHREAD_MUTEX_NORMAL:普通锁,不支持递归,一个线程多次加锁会导致死锁。
  2. PTHREAD_MUTEX_RECURSIVE:递归锁,支持同一线程多次加锁,只有最后一个解锁时才会释放锁。
  3. PTHREAD_MUTEX_ERRORCHECK:检错锁,如果同一线程多次加锁,会返回错误而不是导致死锁。
  4. PTHREAD_MUTEX_DEFAULT:根据实现的选择来确定锁的类型,默认是普通锁。

在iOS开发中,pthread_mutex_t是一种比较底层的线程同步机制,通常较少直接使用。

互斥锁NSLock

NSLock是Foundation框架中的一种互斥锁,用于多线程同步。它是对pthread_mutex_t互斥锁的一种Objective-C封装,提供了更简单易用的接口来实现线程同步。
NSLock是非递归锁,即同一线程多次加锁会导致死锁。如果一个线程在未解锁的情况下再次尝试加锁,会导致死锁。

递归锁NSRecursiveLock

性能较互斥锁略差,适用于同一线程需要多次加锁的情况,支持同一线程多次加锁,不会产生死锁。
@ synchronized 关键字实际上用的是NSRecursiveLock,是系统帮创建使用。

条件锁NSCondition

NSCondition允许一个或多个线程等待某个条件成立,当条件成立时,它可以唤醒等待的线程。NSCondition通常与NSLock配合使用,以实现更复杂的线程同步需求。

条件锁NSConditionLock

NSConditionLock是Foundation框架中的一种条件锁,是对NSCondition的进一步封装,用于多线程同步。它允许一个或多个线程等待多个条件中的一个成立,并在条件成立时唤醒等待的线程。相比于NSCondition,NSConditionLock提供了更为方便的接口来管理多个条件的等待和唤醒。

NSConditionLock可以用于实现多个线程按照特定条件的先后顺序执行。它是一个基于整数的锁,通过指定条件的整数值来进行线程的等待和唤醒。

递归锁@synchronized

当一个线程进入@synchronized代码块时,它会尝试获取lockObject对象的锁。如果当前线程已经持有该锁,那么它可以继续进入临界区。如果当前线程没有持有该锁,那么它会等待直到获得锁为止。
当线程多次进入@synchronized代码块时,它仍然只需要一次释放锁。只有在所有进入@synchronized代码块的递归调用都完成后,锁才会被释放。
@synchronized使用简单,但在一些高并发的场景下,可能会存在性能问题。

读写锁pthrad_rwlock_t

读取加锁可以同时多个线程进行,写入同时只能一个线程进行, 等待的线程处于休眠状态;
1 pthread_rwlock_init()初始化一个读写锁;
2 pthread_rwlock_rdlock()读写锁的读取加锁;
3 pthread_rwlock_wrlock()读写锁的写入加锁;
4 pthread_rwlock_unlock()解锁;
5 pthread_rwlock_destroy()销毁锁;

示例代码:

/***********************************pthread_rwlock_t*************************************/
- (void)rwLockType {
    pthread_rwlock_init(&_lock, NULL);
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    __weak typeof(self) weakSelf = self;
    for (int i = 0; i < 100; i ++) {
        ///同时创建多个线程进行写入操作
        dispatch_async(queue, ^{
            [weakSelf lockWriteAction];
        });
        dispatch_async(queue, ^{
            [weakSelf lockWriteAction];
        });
        dispatch_async(queue, ^{
            [weakSelf lockWriteAction];
        });
        
        ///同时创建多个线程进行读操作
        dispatch_async(queue, ^{
            [weakSelf lockReadAction];
        });
        dispatch_async(queue, ^{
            [weakSelf lockReadAction];
        });
        dispatch_async(queue, ^{
            [weakSelf lockReadAction];
        });
    }
}
- (void)lockReadAction {
    pthread_rwlock_rdlock(&_lock);
    sleep(1);
    NSLog(@"RWLock Read Action   %@", [NSThread currentThread]);
    pthread_rwlock_unlock(&_lock);
}
- (void)lockWriteAction {
    pthread_rwlock_wrlock(&_lock);
    sleep(1);
    NSLog(@"RWLock Write Action   %@", [NSThread currentThread]);
    pthread_rwlock_unlock(&_lock);
}
- (void)dealloc {
    pthread_rwlock_destroy(&_lock);
}
/***********************************pthread_rwlock_t*************************************/

信号量dispatch_semaphore

本来使用于控制线程的最大并发数量的, 我们将dispatch_semaphore_create()并发数量设置为1也可以认为是加锁的功能;
判断信号量的值dispatch_semaphore_wait()如果大于0, 则可以继续往下执行(同时信号量的值减去一),如果信号量的值为0, 则线程进入休眠状态等待(此方法的第二个参数就是设置要等多久, 一般都是使用永久DISPATCH_TIME_FOREVER) ;
释放信号量dispatch_semaphore_signal()同时使信号量的值加上一;

#import "ViewController10.h"
@interface ViewController10 ()
@property (nonatomic, strong) dispatch_semaphore_t semaphore;
@end
@implementation ViewController10
- (void)viewDidLoad {
    [super viewDidLoad];
    self.semaphore = dispatch_semaphore_create(1);
    [self handleMoney];
}
- (void)handleMoney {
    self.money = 100;
    __weak typeof(self) weakSelf = self;
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i ++) {
            [weakSelf saveMoney];
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i ++) {
            [weakSelf drawMoney];
        }
    });
}
- (void)saveMoney{
    dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
    [super saveMoney];
    dispatch_semaphore_signal(self.semaphore);
}
- (void)drawMoney {
    dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
    [super drawMoney ];
    dispatch_semaphore_signal(self.semaphore);
}
@end

异步栅栏dispath_barrier_async

dispatch_queue_create()创建并发队列;
dispatch_barrier_async()异步栅栏;

传入的并发队列队列必须是手动创建, dispatch_queue_create()方式;
如果传入串行队列或者通过dispatch_get_global_queue()方式创建, 则dispath_barrier_async的作用就跟dispath_async变得一样;


/*********************************dispatch_barrier_async**********************************/
- (void)barrierAsyncType {
     self.queue = dispatch_queue_create("rw_queue", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i < 100; i ++) {
        ///同时创建多个线程进行写入操作
        [self barrierWriteAction];
        [self barrierWriteAction];
        [self barrierWriteAction];
        ///同时创建多个线程进行读取操作
        [self barrierReadAction];
        [self barrierReadAction];
        [self barrierReadAction];
    }
}
- (void)barrierReadAction {
    dispatch_async(self.queue, ^{
     sleep(1);
    NSLog(@"barrier Read Action   %@", [NSThread currentThread]);
    });
 }
- (void)barrierWriteAction {
    dispatch_barrier_async(self.queue, ^{
     sleep(1);
    NSLog(@"barrier Write Action   %@", [NSThread currentThread]);
    });
 }
/*********************************dispatch_barrier_async**********************************/

总结

效率排序
1)os_unfair_lock(iOS10之后)
2)OSSpinLock(iOS10之前)
3)dispatch_semaphore(iOS版本兼容性好)
4)pthread_mutex_t(iOS版本兼容行好)
5)NSLock( 基于pthread_mutex_t封装)
6)NSCondition( 基于pthread_mutex_t封装)
7)pthread_mutex_t(recursive)(递归锁的优先推荐)
8)NSRecursiveLock(基于pthread_mutex_t封装)
9)NSConditionLock(基于NSCondition封装)
@synchronized
10.1 iOS12之前基于pthread_mutex_t封装
10.2 iOS12之后基于os_unfair_lock封装(iOS12之后它的效率应该不是最低, 应该在3/4左右);

注意

加锁和解锁的实现一定要配套出现, 不然就会出现死锁的现象;

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容

  • UIKit和Foundation的线程安全 在UIKit和Foundation中,有很多操作都是非线程安全的,比如...
    Jason1226阅读 526评论 0 0
  • iOS中的锁 前言 写在前面: 临界区:指的是一块对公共资源进行访问的代码,并非一种机制或是算法。 自旋锁:是用于...
    ROBIN2015阅读 890评论 0 7
  • 1、锁的归类 锁的分类只有两大类自旋锁和和互斥锁。这两大类下又分成很多不同的小类。了解锁之前建议先了解一下线程及线...
    希尔罗斯沃德_董阅读 1,964评论 2 4
  • 在多线程中,当多个线程同时访问同一块资源的时候,就容易引起数据错乱和数据安全问题 (1).OSSpinLock O...
    雪碧童鞋阅读 285评论 0 0
  • 在多线程一:GCD[//www.greatytc.com/p/4edf7c930095]中我们详细了解了...
    小心韩国人阅读 857评论 0 1