ios 原子属性atomic加锁性能与锁对比, 不推荐的原因
在设置属性的时候, 我们都会写上nonatomic, 基本没有写过atomic, 苹果建议这么做, 因为atomic严重影响了性能.
但是atomic影响了多少? 如果一个属性真的需要在多线程下读写, 那么使用atomic和其他锁之间的性能差多少?
atomic的底层实现只是在 setter 和 getter 方法中加了一个锁, 这个锁表面上是自旋锁, 但是在最新的runtime版本上是一个os_unfair_lock。
但是spinlock_t从名字上看是一个自旋锁, 但是真正对应的类实现中采用的是 os_unfair_lock, 这波操作真的是挂羊头卖狗肉。
写下测试代码测试100W次, 让atomic和信号量 | unfair_lock | NSLock | synchronized 进行PK, 看看到底那个的效率更高.
最终结果, 按照升序排列后, 可以看到 原子性的效率最高, 然后是unfair_lock, 而 synchronized 果然是效率最低, 处于鄙视链的底端.
还是用图更直观的感受,
单独来看, atomic的加锁效率是很高的啊, 为什么苹果不推荐使用atomic了? 个人总结为以下2点 ,更详细的原因和原理参考 : ios 原子属性nonatomic/atomic
- 系统用一个全局生效的字典保存属性值与atomic锁的对应关系, 系统最多提供8个atomic锁给atomic加锁解锁, 当一个项目中有几万个属性都是原子性的时候, 几万映射到8个锁上, 很多属性都会对应到同一把锁上, 那么这个属性就得等其他毫不相关的属性完成读写, 自己才能进行操作, 而且set/get是一个特别高频的操作.
- atomic只针对特定场景保证线程安全, 存在局限性. 只能保证set/get的线程安全, 对于更大范围的线程安全是无法保证的.
举一个很简单的例子,假设定义属性 NSInteger i 是原子的,对i进行 i = i + 1;
这个操作就是不安全的。因为原子性只能保证读写安全,而该表达式需要三步操作:
1.先进行get, 读取i的值存入寄存器;
2.将寄存器的值加1;
3.使用寄存器修改后的值给i赋值;
atomic只能保证1和3是线程安全的, 如果在第1步完成的时候,i被其他线程修改了,那么表达式执行的结果就会与预期的不一样,也就是不安全的。所以要解决这样的线程安全问题, 只能对 整个表达式进行加锁, 单纯对i 设置atomic达不到预期的.
综上, atomic在大范围使用的情况下效率低下, 而且效果不太好, 存在局限性, 所以苹果把线程安全的责任交给了开发者, 由开发者来保证线程安全.
经常看到网上有对比加锁/解锁效率,这次我也借这个机会简单对比下, 加锁解锁1000W次, 数据如下
最后, 本次测试使用设备为iPhone 6S, 系统为iOS 14.0, 下面是测试的代码
#import "ViewController.h"
@import Darwin.os.lock;// 循环1000W次
static const NSInteger TestCount = 10000 * 1000;@interface ViewController ()
// 使用原子性的锁
@property (atomic, strong) NSObject *obj_atomic;
// 外界加锁
@property (nonatomic, strong) NSObject *obj_nonatomic;
@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];CFTimeInterval start = 0;CFTimeInterval end = 0;// 原子性加锁解锁100W次,耗时:0.23Sstart = CACurrentMediaTime();[self testAtomic];end = CACurrentMediaTime();NSLog(@"原子性加锁解锁%zd次,耗时:%.2lfS",TestCount,end-start);// 信号量加锁解锁100W次,耗时:0.54Sstart = CACurrentMediaTime();[self testSemaphore];end = CACurrentMediaTime();NSLog(@"信号量加锁解锁%zd次,耗时:%.2lfS",TestCount,end-start);// NSLock加锁解锁100W次,耗时:0.52Sstart = CACurrentMediaTime();[self testLock];end = CACurrentMediaTime();NSLog(@"NSLock加锁解锁%zd次,耗时:%.2lfS",TestCount,end-start);// UnfairLock加锁解锁100W次,耗时:0.30Sstart = CACurrentMediaTime();[self testUnfairLock];end = CACurrentMediaTime();NSLog(@"UnfairLock加锁解锁%zd次,耗时:%.2lfS",TestCount,end-start);// Synchronized加锁解锁100W次,耗时:0.70Sstart = CACurrentMediaTime();[self testSynchronized];end = CACurrentMediaTime();NSLog(@"Synchronized加锁解锁%zd次,耗时:%.2lfS",TestCount,end-start);
}- (void)testAtomic {for (NSInteger i = 0; i < TestCount; i++) {self.obj_atomic = [[NSObject alloc] init];}for (NSInteger i = 0; i < TestCount; i++) {[self.obj_atomic class];}
}- (void)testSemaphore {dispatch_semaphore_t sem = dispatch_semaphore_create(1);for (NSInteger i = 0; i < TestCount; i++) {dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);self.obj_nonatomic = [[NSObject alloc] init];dispatch_semaphore_signal(sem);}for (NSInteger i = 0; i < TestCount; i++) {dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);[self.obj_nonatomic class];dispatch_semaphore_signal(sem);}
}- (void)testLock {NSLock *lock = [[NSLock alloc] init];for (NSInteger i = 0; i < TestCount; i++) {[lock lock];self.obj_nonatomic = [[NSObject alloc] init];[lock unlock];}for (NSInteger i = 0; i < TestCount; i++) {[lock lock];[self.obj_nonatomic class];[lock unlock];}
}- (void)testUnfairLock {os_unfair_lock_t unfair_lock = &(OS_UNFAIR_LOCK_INIT);for (NSInteger i = 0; i < TestCount; i++) {os_unfair_lock_lock(unfair_lock);self.obj_nonatomic = [[NSObject alloc] init];os_unfair_lock_unlock(unfair_lock);}for (NSInteger i = 0; i < TestCount; i++) {os_unfair_lock_lock(unfair_lock);[self.obj_nonatomic class];os_unfair_lock_unlock(unfair_lock);}
}- (void)testSynchronized {for (NSInteger i = 0; i < TestCount; i++) {@synchronized (self) {self.obj_nonatomic = [[NSObject alloc] init];}}for (NSInteger i = 0; i < TestCount; i++) {@synchronized (self) {[self.obj_nonatomic class];}}
}@end
ios 原子属性atomic加锁性能与锁对比, 不推荐的原因相关推荐
- iOS - 线程中常见的几种锁
线程锁主要是用来解决"共享资源"的问题,实际开发中或多或少的都会用到各类线程锁,为了线程的安全我们有必要了解常见的几种锁,下面是本人查看一些大牛的博客然后整理的内容,加上自己的一些 ...
- iOS进阶之atomic一定是线程安全的吗(10)
IOS项目中nonatomic和atomic分析 //有两个属性,分别设置为nonatomic和atomic #import <UIKit/UIKit.h>@interface ViewC ...
- ios 线程休眠_iOS底层分析 - 线程锁(一)NSLock
大纲 常用锁介绍 自旋锁和互斥锁的一些问题 NSLock及源码分析 NSLock 坑 一.常用锁介绍 锁的目的是为了解决资源抢夺 iOS中的常用的锁有如下几种: 1.自旋锁: 使用与多线程同步的一种锁 ...
- 偏向锁、轻量级锁、重量级锁加锁过程即锁升级膨胀过程
偏向锁.轻量级锁.重量级锁加锁过程即锁升级膨胀过程 synchronized 偏向锁 为什么要引入偏向锁 偏向锁加锁过程 线程获取到锁对象的偏向锁之后,执行完同步代码块之后,会释放这个偏向锁吗 使用了 ...
- 高性能iOS开发--移动应用的性能
打算把<高性能iOS开发>这本书公开出来,供大家学习.这是第一章,感兴趣的可以订阅我的专题 高性能iOS应用开发. 本书假设你是 iOS 开发人员,有长期开发原生 iOS 应用的经验,并且 ...
- iOS 使用Instruments优化内存性能
iOS 使用Instruments优化内存性能 问题 项目中使用到图片合成视频,发现内存增长十分的迅速,导致一些因为内存引起的问题,本文使用这个案例,结合Instruments工具检测和分析问题,最终 ...
- python 多人连接mysql 进行事务操作 对mysql加锁与释放锁
python 多人连接mysql 对mysql进行事务操作 对mysql加锁与释放锁 下面这个是user1代码块 # -*- coding: utf-8 -*- # user1 import pymy ...
- 如何修改3D模型的原子属性
Chem3D是专门用于绘制化学三维模型和进行计算化学数据的ChemOffice组件,在三维模型中每个原子都有众多属性,比如原子类型.原子符号.原子编号以及原子颜色等等.掌握Chem 3D模型的原子属性 ...
- 白鹭引擎用java_白鹭引擎发布 5.1.6 版 优化打包 iOS Android App 的运行性能
原标题:白鹭引擎发布 5.1.6 版 优化打包 iOS Android App 的运行性能 在3月12日,我们将为白鹭引擎推出5.1.6 版本.5.1.6版本是对去年12月份发布的5.1版本的一次功能 ...
- java synchronized加锁 或者redis锁不起作用
前几天在代码中需要加锁 使用了synchronized 以及redis锁均一直不起作用 , 寻找几天后发现是因为在Controller层加了@Transactional(rollbackFor = E ...
最新文章
- 计算机音乐戏子多秋,抖音戏子多秋是什么歌
- 河科大c语言上机实验答案,2016年河南科技学院信息工程学院C语言上机编程考研复试题库...
- LSTM模型在问答系统中的应用
- 通过流程构建组织的【个人能力】和【团队能力】
- maven笔记(2)-- 构建Java Project 及 Maven命令使用
- 持久化保存iptables规则
- 下岗工人到达退休年龄,养老保险未缴纳满15年,补缴合适吗?
- 【NLPCC 2020】Call for Participation: Shared Tasks in NLPCC 2020
- vesamenu.c32:not a COM32R image报错解决方案
- 年底将至 怎么向国外客户开口催单 附话术模板
- Kindle Paperwhite2测评剧本.
- mysql添加用户并赋予权限命令
- 堆米微信H5页面怎么制作?易企秀微信H5页面制作,微信简历制作,
- 深入了解机器学习(Descending into ML):线性回归
- 边缘计算的下一场革命:1+12?
- python车辆定位调度管理系统,基于django+twisted
- 第三方推送服务:个推服务推送流程
- vue3图片描点标记
- 【Java基础1】Java开发工具包JDK
- Axure 团队原型创建
热门文章
- TXT阅读神器 分分钟打造一本电子书
- 代码对比工具Sublime——Sublimerge
- Java Web实战详细教程(一)系列介绍+环境搭建
- 计算机上安装了更新ie版本,安装ie浏览器提示系统有更新的版本怎么办_ie提示有更新版本的解决方法...
- 使用java,求100以内的质数(素数)
- vmware workstation 12 密钥
- sklearn做文本聚类分析
- SosoApi,编辑Swagger UI的神器
- spring常用注解使用讲解
- 百战学堂python教学文档_尚学堂百战程序员:python对文件的操作