iOS 多线程编程的安全问题
来源:互联网 发布:淘宝二手手机3c认证 编辑:程序博客网 时间:2024/05/22 06:54
首先我们从属性说起,理解多线程为什么不安全?
多线程共享状态可以共同访问某个对象的属性(property),我们都知道给property加上atomic attribute之后,一定程度上可以保证多线程安全:
@property (copy, atomic) NSString *userName;
这样写,多线程就真的安全吗?
@property (assign, atomic) int count;- (void)testAtomic { NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(thread1Action) object:self]; thread1.name = @"thread1"; NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(thread2Action) object:self]; thread2.name = @"thread2"; [thread1 start]; [thread2 start];}- (void)thread1Action { for (int i = 0; i < 10000; i++) { self.count += 1; NSLog(@"%@:%d",[NSThread currentThread].name, self.count); }}- (void)thread2Action { for (int i = 0; i < 10000; i++) { self.count += 1; NSLog(@"%@:%d",[NSThread currentThread].name, self.count); }}
经过测试,即使将count声明为atomic,也很难保证最后的结果是20000。
愿意就是 self.count += 1这句并不是原子操作,我们声明count为atomic,意味着count的setter和getter方法都是原子操作。程序在执行这句代码的时候,其实至少包含了读,写操作,当前线程写的时候,另一个线程可能已经写了好多次数据了,导致最后的结果值小于预期值。这种场景我们就可以任务是多线程不安全的。
@property (strong, atomic) NSArray *array;- (void)testAtomic { NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(thread1Action) object:self]; thread1.name = @"thread1"; NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(thread2Action) object:self]; thread2.name = @"thread2"; [thread1 start]; [thread2 start];}- (void)thread1Action { for (int i = 0; i < 1000000; i++) { if (i % 2 == 0) { self.array = @[@"1", @2, @YES]; } else { self.array = @[@"1"]; } } NSLog(@"%@:%@",[NSThread currentThread].name, self.array);}- (void)thread2Action { for (int i = 0; i < 1000000; i++) { if ([self.array count] >= 2) { [self.array objectAtIndex:1]; } } NSLog(@"%@:%@",[NSThread currentThread].name, self.array);}
上面我们对集合做了测试,同样声明为atomic,虽然我们在访问数组元素之前,做了count判断,但thread2依然很容易crash。
通过测试,我们发现即使声明为atomic也不能保证多线程安全,其作用只是给setter和getter方法加了个锁,只能保证代码进入setter或getter方法内存时安全的,一旦出了存取方法就不在起作用,所以atomic属性和使用property的多线程并没有直接联系。另外,atomic由于加锁也会带来一些性能损耗,所以我们在声明属性的时候,一般声明为nonatomic,在需要做多线程安全的场景,需要我们去额外加锁做同步。我们平常APP出现莫名其妙难以重现的多线程crash多是这一类,所以我们在多线程的场景下访问这类内存区域时要多加小心。
那么,平时我们做才能保证多线程安全呢?
简单点,只要保证原子性就可以。原子性可以保证代码串行执行,保证在执行的过程中,不会有其他线程介入。
@property (strong, atomic) NSArray *array;@property (strong, nonatomic) NSLock *lock;- (void)testAtomic { self.lock = [[NSLock alloc] init]; NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(thread1Action) object:self]; thread1.name = @"thread1"; NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(thread2Action) object:self]; thread2.name = @"thread2"; [thread1 start]; [thread2 start];}- (void)thread1Action { [self.lock lock]; for (int i = 0; i < 1000000; i++) { if (i % 2 == 0) { self.array = @[@"1", @2, @YES]; } else { self.array = @[@"1"]; } NSLog(@"%@:%@",[NSThread currentThread].name, self.array); } [self.lock unlock];}- (void)thread2Action { [self.lock lock]; for (int i = 0; i < 1000000; i++) { if ([self.array count] >= 2) { [self.array objectAtIndex:1]; } NSLog(@"%@:%@",[NSThread currentThread].name, self.array); } [self.lock unlock];}
iOS常用的加锁方式有一下几种:
- @synchronized(token)
- NSLock
- dispath_semaphore_t
上面里示例就是使用NSLock,当然还有什么条件锁,递归锁等等,大家可自行学习,以下是使用其余两种的示例:
- (void)testDispathSemaphore { dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_semaphore_t derma = dispatch_semaphore_create(1); NSMutableArray *array = [NSMutableArray array]; for (int i = 0; i < maxCount; i++) { dispatch_async(globalQueue, ^{ dispatch_semaphore_wait(derma, DISPATCH_TIME_FOREVER);//-1 [array addObject:@(i)]; dispatch_semaphore_signal(derma);//+1 }); } NSLog(@"%@", array);}- (void)testSynchronized { dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0); NSMutableArray *array = [NSMutableArray array]; for (int i = 0; i < maxCount; i++) { dispatch_async(globalQueue, ^{ @synchronized (array) { [array addObject:@(i)]; } }); } NSLog(@"%@", array);}
0 0
- iOS 多线程编程的安全问题
- iOS 多线程~安全问题
- 多线程(多线程的安全问题)
- 多线程的安全问题
- STL的多线程安全问题
- STL的多线程安全问题
- STL的多线程安全问题
- 多线程的运行安全问题
- mysql的多线程安全问题
- 多线程的安全问题
- 多线程的安全问题
- 多线程的安全问题
- mysql的多线程安全问题
- 多线程的安全问题
- 多线程安全问题的解决方法
- Java 多线程的安全问题
- 多线程的安全问题
- STL的多线程安全问题
- 深度学习中的Data Augmentation方法
- C++类对象在各种内存区的构造函数析构函数执行顺序
- 移动端样式配置
- 源码分析-ArrayBlockingQueue
- C# 筛选并删除某一类文件夹
- iOS 多线程编程的安全问题
- 准备面试需要知道的经典算法--堆排序
- BFS(广搜) 骑士旅行
- DevStack环境搭建
- 解决MINGW64、Git Bash命令不能回退的问题
- 一篇故事讲述了计算机网络里的基本概念:网关,DHCP,IP寻址,ARP欺骗,路由,DDOS等
- AnInterestingSequence
- 利用tensorflow制作一个简单的聊天机器人
- spring MVC国际化的简单实现