本文主要是介绍理解:iOS开发中锁的实现原理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
摘要
本文的目的不是介绍 iOS 中各种锁如何使用,一方面笔者没有大量的实战经验,另一方面这样的文章相当多,比如 iOS中保证线程安全的几种方式与性能对比、iOS 常见知识点(三):Lock。本文也不会详细介绍锁的具体实现原理,这会涉及到太多相关知识,笔者不敢误人子弟。
本文要做的就是简单的分析 iOS 开发中常见的几种锁如何实现,以及优缺点是什么,为什么会有性能上的差距,最终会简单的介绍锁的底层实现原理。水平有限,如果不慎有误,欢迎交流指正。同时建议读者在阅读本文以前,对 OC 中各种锁的使用方法先有大概的认识。
在 ibireme 的 不再安全的 OSSpinLock 一文中,有一张图片简单的比较了各种锁的加解锁性能:
本文会按照从上至下(速度由快至慢)的顺序分析每个锁的实现原理。需要说明的是,加解锁速度不表示锁的效率,只表示加解锁操作在执行时的复杂程度,下文会通过具体的例子来解释。
OSSpinLock
上述文章中已经介绍了 OSSpinLock 不再安全,主要原因发生在低优先级线程拿到锁时,高优先级线程进入忙等(busy-wait)状态,消耗大量 CPU 时间,从而导致低优先级线程拿不到 CPU 时间,也就无法完成任务并释放锁。这种问题被称为优先级反转。
为什么忙等会导致低优先级线程拿不到时间片?这还得从操作系统的线程调度说起。
现代操作系统在管理普通线程时,通常采用时间片轮转算法(Round Robin,简称 RR)。每个线程会被分配一段时间片(quantum),通常在 10-100 毫秒左右。当线程用完属于自己的时间片以后,就会被操作系统挂起,放入等待队列中,直到下一次被分配时间片。
自旋锁的实现原理
自旋锁的目的是为了确保临界区只有一个线程可以访问,它的使用可以用下面这段伪代码来描述:
do { Acquire LockCritical section // 临界区Release LockReminder section // 不需要锁保护的代码
}
在 Acquire Lock 这一步,我们申请加锁,目的是为了保护临界区(Critical Section) 中的代码不会被多个线程执行。
自旋锁的实现思路很简单,理论上来说只要定义一个全局变量,用来表示锁的可用情况即可,伪代码如下:
bool lock = false; // 一开始没有锁上,任何线程都可以申请锁
do { while(lock); // 如果 lock 为 true 就一直死循环,相当于申请锁lock = true; // 挂上锁,这样别的线程就无法获得锁Critical section // 临界区lock = false; // 相当于释放锁,这样别的线程可以进入临界区Reminder section // 不需要锁保护的代码
}
注释写得很清楚,就不再逐行分析了。可惜这段代码存在一个问题: 如果一开始有多个线程同时执行 while 循环,他们都不会在这里卡住,而是继续执行,这样就无法保证锁的可靠性了。解决思路也很简单,只要确保申请锁的过程是原子操作即可。
作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS交流群:519832104 不管你是小白还是大牛欢迎入驻,分享经验,讨论技术,大家一起交流学习成长!
另附上一份各好友收集的大厂面试题,需要iOS开发学习资料、面试真题,可以添加iOS开发进阶交流群,进群可自行下载!
这篇关于理解:iOS开发中锁的实现原理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!