首页 > 代码库 > iOS核心笔记——多线程-原子/非原子属性

iOS核心笔记——多线程-原子/非原子属性

1、原子属性:

1-1、nonatomic与atomic:
  • nonatomic : 非原子属性;
  • atomic : 原子属性;
    • 线程安全的,针对多线程设计的属性修饰符,是默认值.
    • 保证同一时间只有一个线程能够写入,但是同一个时间多个线程都可以读取;
    • 单写多读 : 单个线程写入write,多个线程可以读取read
    • atomic 本身就有一把锁,自旋锁

1-2、nonatomicatomic对比:
  • nonatomic : 非线程安全,适合内存小的移动设备;
  • atomic : 线程安全,需要消耗大量的资源.性能比非原子属性要差。

1-3、iOS开发的建议:
  • 所有属性都声明为nonatomic,性能更高;
  • 尽量避免多线程抢夺同一块资源;
  • 尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力。

2、模拟原子属性:

2-1、定义属性:
1./// 非原子属性
2.@property (nonatomic,strong) NSObject *obj1;
3./// 原子属性:内部有"自旋锁"
4.@property (atomic,strong) NSObject *obj2;
5./// 模拟原子属性
6.@property (atomic,strong) NSObject *obj3;

?了解: 重写非原子属性的settergetter方法:

?了解: 1、重写了原子属性的setter方法之后,会覆盖原子属性内部的自旋锁,使其失效;然后我们加入互斥锁,来模拟单写多读

?了解: 2、重写了属性的settergetter方法之后,系统就不会再帮我们生成待下划线的成员变量;使用合成指令@synthesize,就可以手动的生成带下划线的成员变量。


示例程序:

1.// 合成指令
2.@synthesize obj3 = _obj3;
3.
4./// obj3的setter方法
5.- (void)setObj3:(NSObject *)obj3
6.{
7. @synchronized(self) {
8. _obj3 = obj3;
9. }
10.}
11.
12./// obj3的getter方法
13.- (NSObject *)obj3
14.{
15. return _obj3;
16.}

2-2、性能测试:
1./// 测试"非原子属性","互斥锁","自旋锁"的性能
2.- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
3.{
4. NSInteger largeNum = 1000*1000;
5.
6. NSLog(@"非原子属性");
7. CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
8. for (int i = 0; i < largeNum; i++) {
9. self.obj1 = [[NSObject alloc] init];
10. }
11. NSLog(@"非原子属性 => %f",CFAbsoluteTimeGetCurrent()-start);
12.
13. NSLog(@"原子属性");
14. start = CFAbsoluteTimeGetCurrent();
15. for (int i = 0; i < largeNum; i++) {
16. self.obj2 = [[NSObject alloc] init];
17. }
18. NSLog(@"原子属性 => %f",CFAbsoluteTimeGetCurrent()-start);
19.
20. NSLog(@"模拟原子属性");
21. start = CFAbsoluteTimeGetCurrent();
22. for (int i = 0; i < largeNum; i++) {
23. self.obj3 = [[NSObject alloc] init];
24. }
25. NSLog(@"模拟原子属性 => %f",CFAbsoluteTimeGetCurrent()-start);
26.}

3、互斥锁和自旋锁对比:

3-1、共同点:
  • 都能够保证同一时间,只有一条线程执行锁定范围的代码
3-2、不同点:
  • 互斥锁:如果发现有其他线程正在执行锁定的代码,线程会进入休眠状态,等待其他线程执行完毕,打开锁之后,线程会重新进入就绪状态;等待被CPU重新调度。
  • 自旋锁:如果发现有其他线程正在执行锁定的代码,线程会以死循环的方式;一直等待锁定代码执行完成。

4、开发建议:

    1. 所有属性都声明为nonatomic,原子属性和非原子属性的性能几乎一样。
    2. 尽量避免多线程抢夺同一块资源。
    3. 要实现线程安全,必须要用到;无论什么锁,都是有性能消耗的。
    4. 自旋锁更适合执行非常短的代码,死循环内部不适合写复杂的代码。
    5. 尽量将加锁,资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力。
    6. 为了流畅的用户体验,UIKit类库的线程都是不安全的;所以我们需要在主线程(UI线程)上更新UI。
    7. 所有包含NSMutable的类都是线程不安全的,在做多线程开发的时候,需要注意多线程同时操作可变对象的线程安全问题。

iOS核心笔记——多线程-原子/非原子属性