atomic线程安全吗?

1.什么是线程安全?

线程安全,是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。

2.atomic与nonatomic的区别

atomic 与 nonatomic 的本质区别在于gettersetter方法实现上的不同:

#示例1:nonatomic属性的实现

1
2
3
4
5
6
7
8
9
10
11
12
//@property(nonatomic, retain) NSMutableArray *mutArr;
- (NSMutableArray *)mutArr
{
return _mutArr;
}

- (void)setMutArr:(NSMutableArray *)newArr
{
[newArr retain];
[_mutArr release];
_mutArr = newArr;
}

#示例2:atomic属性的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//@property(retain) NSMutableArray *mutArr;
- (NSMutableArray *)mutArr
{
@synchronized(self) {
return _mutArr;
}
}

- (void)setMutArr:(NSMutableArray *)newArr
{
@synchronized(self) {
[_mutArr release];
_mutArr = [newArr retain];
}
}

nonatomic 修饰的对象不保证settergetter的完整性,多线程访问它时可能会返回未初始化的对象。nonatomic 比 atomic 快,但它不是线程安全的。

atomic 修饰的对象会保证settergetter的完整性,任何线程对其访问都可以得到一个完整的初始化后的对象。例如有多个线程同时调用setter,不会出现某个线程执行完setter全部语句之前,另一个线程开始执行setter情况,相当于函数头尾加了锁一样。

3.atomic与真正的线程安全

atomic 只保证了属性的存取方法是完整的,但并不保证整个属性”对象”是线程安全的,因为别的线程还能进行其他操作。比如,线程A执行 [self.mutArr addObject:obj];,同一时间线程B执行self.mutArr[index]就不是线程安全的。多个线程同时调用settergetter时获得的对象值可能会不一样,甚至发生崩溃。想要绝对的线程安全就要用到线程的同步机制,比如使用NSLock@synchronized等加锁。

#示例3:数据同步(加锁)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
@interface AppDelegate()
@property (atomic, strong) NSMutableArray *mArr;
@end


@implementation AppDelegate

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.mArr = [NSMutableArray arrayWithObjects:@"1",@"2",@"3",@"4", nil];

NSOperationQueue * queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 2;

//NSLock *aLock = [[NSLock alloc] init];

__weak typeof(self) weakSelf = self;
NSBlockOperation * operation1 = [NSBlockOperation blockOperationWithBlock:^{
__strong typeof(weakSelf) strongSelf = weakSelf;
//[aLock lock];
@synchronized (self){
for(NSString * str in strongSelf.mArr){
NSLog(@"1读数据:%@",str);
}
}
//[aLock unlock];
}];

NSBlockOperation * operation2 = [NSBlockOperation blockOperationWithBlock:^{
__strong typeof(weakSelf) strongSelf = weakSelf;
//[aLock lock];
@synchronized (self){
for (NSInteger i = 0; i < 10; i++) {
NSString *str = [NSString stringWithFormat:@"%@%ld",@"字符串",i];
NSLog(@"++++2写数据:%@",str);
[strongSelf.mArr addObject:str];
}
}
//[aLock unlock];
}];

NSBlockOperation * operation3 = [NSBlockOperation blockOperationWithBlock:^{
__strong typeof(weakSelf) strongSelf = weakSelf;
//[aLock lock];
@synchronized (self){
for(NSString * str in strongSelf.mArr){
NSLog(@"3读数据:%@",str);
}
}
//[aLock unlock];
}];

[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];

return YES;
}

示例中,数组被定义为 atomic 的属性,但是在不加锁的情况下,会出现崩溃,报错“Collection <__NSArrayM: 0x60000024a8f0> was mutated while being enumerated.”,说明 atomic 对象并不是线程安全的。示例中提供了两种数据同步的方式,一种是使用NSLock进行加锁,另一种是使用@synchronized加锁,两种都可以保证数组对象的线程安全。

综上,在无需考虑多线程的情况下使用 nonatomic,它能让编译器少生成一些加锁的代码,提高读写效率。在多线程环境下使用 atomic,同时配合加锁,防止产生该变量读写等操作的同步问题。


atomic线程安全吗?
https://davidlii.cn/2017/11/24/atomic.html
作者
Davidli
发布于
2017年11月24日
许可协议