iOS学习-锁

在 iOS 中,锁是多线程编程中的关键要素,也是保证线程安全的重要手段之一。本文将介绍常见的几种锁及其使用场景,并提供相关的代码示例。

iOS中的锁主要可以分为两大类,互斥锁和自旋锁,其他锁都是这两种锁的延伸和扩展。

一、自旋锁

原理

自旋锁是一种基于忙等待的锁,当多个线程访问共享资源时,自旋锁会不停地进行循环检查,直到获取到锁为止。自旋锁的好处在于它避免了线程切换和上下文切换的开销,在多核 CPU 上,自旋锁可以充分利用 CPU 时间片,因此在锁竞争不激烈的情况下,自旋锁的性能比互斥锁好。

使用场景

自旋锁适用于以下场景:

  • 多个线程访问共享资源的竞争不激烈,即锁竞争不激烈。
  • 访问共享资源的时间很短,即不会发生线程挂起。

iOS只有一种自旋锁:OSSpinLock,其使用方法如下:

#import <libkern/OSAtomic.h>

OSSpinLock spinLock = OS_SPINLOCK_INIT;

// 获取锁
OSSpinLockLock(&spinLock);

// 临界区代码

// 释放锁
OSSpinLockUnlock(&spinLock);

需要注意的是,由于自旋锁等待线程不会进入阻塞状态,而是不断尝试获取锁,如果等待时间过长会导致CPU占用过高,从而影响应用程序的性能。此外,OSSpinLock还存在优先级反转的问题。

优先级反转指的是高优先级的线程因等待低优先级线程所持有的锁而被阻塞的情况。在这种情况下,高优先级的线程可能一直等待,直到低优先级的线程释放锁。并且高优先级的线程会抢占CPU资源,低优先级的线程得不到CPU调度,进而延长了等待时间,导致高优先级的任务被延迟执行。

由于这些原因,OSSpinLock已被苹果废弃。

二、互斥锁

互斥锁是一种常见的锁,用于保护临界区代码,防止多个线程同时访问共享资源。

与自旋锁不同的是,互斥锁会将未获得锁的线程挂起,等待锁的释放。这种操作需要进行上下文切换,开销较大,因此互斥锁的性能不如自旋锁。

iOS 中的常见互斥锁包括 pthread_mutex_t、NSLock、NSCondition、NSConditionLock、NSRecursiveLock等。

1.pthread_mutex_t

pthread_mutex是一种互斥锁,它可以保证同一时间只有一个线程可以访问共享资源。pthread_mutex基于POSIX标准实现。

pthread_mutex与其他锁机制相比,具有以下特点:

  • 轻量级,适合高并发场景。
  • 由于是C语言的库,使用起来相对较复杂。

代码示例

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

// 获取锁
pthread_mutex_lock(&mutex);

// 临界区代码

// 释放锁
pthread_mutex_unlock(&mutex);

2.NSLock

NSLock是一个简单的互斥锁,它基于POSIX线程互斥锁实现。

代码示例

NSLock *lock = [[NSLock alloc] init];

// 获取锁
[lock lock];

// 临界区代码

// 释放锁
[lock unlock];

3. NSCondition

NSCondition是一种条件锁,它可以让线程在某个条件满足时等待,并在条件满足时唤醒线程。NSCondition基于pthread_cond_t和pthread_mutex_t实现。

NSCondition适用于以下场景:

  • 需要等待某个条件满足后再进行访问的场景。
  • 支持多个条件等待和唤醒的场景。

代码示例

// 初始化条件锁和条件变量
NSCondition *condition = [[NSCondition alloc] init];
BOOL conditionMet = NO;

- (void)waitExample {
    // 等待条件满足
    [condition lock];
    while (!conditionMet) {
        [condition wait];
    }
    // 访问共享资源
    // 释放锁
    [condition unlock];
}

- (void)signalExample {
    // 唤醒等待的线程
    [condition lock];
    conditionMet = YES;
    [condition signal];
    [condition unlock];
}

4. NSConditionLock

NSConditionLock是一种条件锁,它可以让线程在不同的条件下等待。NSConditionLock基于NSCondition和一个内部的状态变量实现。

NSConditionLock与其他锁机制相比,具有以下特点:

  • 支持多个条件等待和唤醒。
  • 支持在不同的条件下等待。

代码示例

// 初始化条件锁
NSConditionLock *conditionLock = [[NSConditionLock alloc] initWithCondition:0];

- (void)conditionLockExample {
    // 等待条件1
    [conditionLock lockWhenCondition:0];

    // 访问共享资源

    // 设置条件2
    [conditionLock unlockWithCondition:1];

    // 等待条件2
    [conditionLock lockWhenCondition:1];

    // 访问共享资源

    // 释放锁
    [conditionLock unlock];
}

5. NSRecursiveLock

NSRecursiveLock是一种递归锁,它允许同一个线程多次获取锁,避免死锁。

NSRecursiveLock与其他锁机制相比,具有以下特点:

  • 支持递归锁,同一线程可以多次获取同一把锁,避免死锁。
  • NSRecursiveLock的性能比NSLock略低,因为它需要维护额外的状态以支持递归锁。

代码示例

// 初始化递归锁
NSRecursiveLock *recursiveLock = [[NSRecursiveLock alloc] init];

- (void)recursiveLockExample {
    // 获取递归锁
    [recursiveLock lock];

    // 访问共享资源

    // 再次获取递归锁
    [recursiveLock lock];

    // 访问共享资源

    // 释放递归锁
    [recursiveLock unlock];

    // 释放递归锁
    [recursiveLock unlock];
}

三. 信号量

原理

信号量是一种常见的并发控制机制,用于控制对共享资源的访问。当多个线程访问共享资源时,信号量可以用来限制并发访问的数量。信号量有一个计数器,每个线程访问共享资源前需要获取信号量,如果计数器为0,则线程需要等待,直到其他线程释放信号量为止。如果计数器不为0,则线程可以访问共享资源,并将计数器减1。当线程访问完共享资源后,需要释放信号量,使计数器加1,以便其他线程可以访问共享资源。

区别

与互斥锁和自旋锁不同的是,信号量可以控制对共享资源的并发访问数量,因此它更适合用于限制并发度较高的情况。

使用场景
  • 多个线程访问共享资源的竞争激烈,即锁竞争激烈。
  • 需要控制并发访问的数量。

代码示例

// 初始化信号量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

- (void)lockExample {
    // 等待信号量
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    // 访问共享资源
    // 释放信号量
    dispatch_semaphore_signal(semaphore);
}

总结

本文介绍了iOS开发中常见的五种锁机制:互斥锁、自旋锁、信号量、读写锁和GCD同步锁。对于每种锁机制,我们讲解了它的原理、区别、使用场景和代码示例,以便开发者可以根据实际情况选择合适的锁机制来实现多线程访问控制。在实际开发中,我们需要根据具体的场景选择合适的锁机制,以保证多线程程序的正确性和效率。

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

推荐阅读更多精彩内容

  • Q:为什么出现多线程? A:为了实现同时干多件事的需求(并发),同时进行着下载和页面UI刷新。对于处理器,为每个线...
    幸福相依阅读 1,582评论 0 2
  • iOS中的锁 前言 写在前面: 临界区:指的是一块对公共资源进行访问的代码,并非一种机制或是算法。 自旋锁:是用于...
    ROBIN2015阅读 894评论 0 7
  • 多线程系列篇章计划内容:iOS多线程编程(一) 多线程基础[https://juejin.im/post/6890...
    卖馍工程师阅读 543评论 0 3
  • 前言 iOS开发中由于各种第三方库的高度封装,对锁的使用很少,刚好之前面试中被问到的关于并发编程锁的问题,都是一知...
    iOS__开发者皮皮峰阅读 556评论 1 5
  • 前言 iOS开发中由于各种第三方库的高度封装,对锁的使用很少,刚好之前面试中被问到的关于并发编程锁的问题,都是一知...
    喵渣渣阅读 3,703评论 0 33