Objective-C的NSMutableArray线程安全吗?
我一直在试图解决这个崩溃近一个星期。 应用程序崩溃,没有任何exception或堆栈跟踪。 在僵尸模式下运行仪器时,应用程序不会以任何方式崩溃。
我有一个方法在不同的线程上被调用。 修复崩溃的解决scheme正在取代
[self.mutableArray removeAllObjects];
同
dispatch_async(dispatch_get_main_queue(), ^{ [self.searchResult removeAllObjects]; });
我认为这可能是一个计时问题,所以我试图同步它,但它仍然崩溃:
@synchronized(self) { [self.searchResult removeAllObjects]; }
这是代码
- (void)populateItems { // Cancel if already exists [self.searchThread cancel]; self.searchThread = [[NSThread alloc] initWithTarget:self selector:@selector(populateItemsinBackground) object:nil]; [self.searchThread start]; } - (void)populateItemsinBackground { @autoreleasepool { if ([[NSThread currentThread] isCancelled]) [NSThread exit]; [self.mutableArray removeAllObjects]; // Populate data here into mutable array for (loop here) { if ([[NSThread currentThread] isCancelled]) [NSThread exit]; // Add items to mutableArray } } }
这是NSMutableArray不是线程安全的问题吗?
没有。
它不是线程安全的,如果你需要从另一个线程修改你的可变数组,你应该使用NSLock
来确保一切按计划进行:
NSLock *arrayLock = [[NSLock alloc] init]; [...] [arrayLock lock]; // NSMutableArray isn't thread-safe [myMutableArray addObject:@"something"]; [myMutableArray removeObjectAtIndex:5]; [arrayLock unlock];
正如其他人所说,NSMutableArray是不是线程安全的。 如果任何人想要在线程安全的环境中实现多于removeAllObject,除了使用锁之外,我还会给出使用GCD的另一个解决scheme。 你必须做的是同步读取/更新(replace/删除)操作。
首先获取全局并发队列:
dispatch_queue_t concurrent_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
阅读:
- (id)objectAtIndex:(NSUInteger)index { __block id obj; dispatch_sync(self.concurrent_queue, ^{ obj = [self.searchResult objectAtIndex:index]; }); return obj; }
对于插入:
- (void)insertObject:(id)obj atIndex:(NSUInteger)index { dispatch_barrier_async(self.concurrent_queue, ^{ [self.searchResult insertObject:obj atIndex:index]; }); }
从Apple Doc关于dispatch_barrier_async:
当屏障块到达专用并发队列的前端时,不会立即执行。 相反,队列等待直到其当前正在执行的块完成执行。 在这一点上,障碍块自行执行。 在屏障块之后提交的任何块都不会执行,直到屏障块完成。
删除类似:
- (void)removeObjectAtIndex:(NSUInteger)index { dispatch_barrier_async(self.concurrent_queue, ^{ [self.searchResult removeObjectAtIndex:index]; }); }
编辑 :其实我今天发现了另一个更简单的方法,通过使用GCD提供的串行队列来同步对资源的访问。
来自Apple Doc 并发编程指南>调度队列 :
串行队列在您希望以特定顺序执行任务时非常有用。 一个串行队列一次只执行一个任务,并且总是从队列的头部抽取任务。 您可以使用串行队列而不是锁来保护共享资源或可变数据结构。 与锁不同,串行队列确保任务按可预测的顺序执行。 只要您将任务asynchronous提交到串行队列,队列就永远不会死锁。
创build您的串行队列:
dispatch_queue_t myQueue = dispatch_queue_create("com.example.MyQueue", NULL);
asynchronous发送到串行队列的任务:
dispatch_async(myQueue, ^{ obj = [self.searchResult objectAtIndex:index]; }); dispatch_async(myQueue, ^{ [self.searchResult removeObjectAtIndex:index]; });
希望能帮助到你!
除了NSLock
也可以使用@synchronized
( condition-object ),你只需要确保数组的每个访问都被包装在一个@synchronized
,同一个对象作为条件对象 ,如果你只想修改内容那么你可以使用数组本身作为条件对象 ,其他明智的你将不得不使用别人,你知道不会消失,父对象,即自我,是一个不错的select,因为它将永远是同一个arrays。
在@property
属性中的primefaces将只会设置数组线程安全而不会修改内容,即self.mutableArray
= …是线程安全的,但[self.mutableArray removeObject:]
不是。
__weak typeof(self)weakSelf = self; @synchronized (weakSelf.mutableArray) { [weakSelf.mutableArray removeAllObjects]; }
这个惊人的文章将解释一切:
由于提到了串行队列:对于一个可变数组,仅仅询问“线程安全”是不够的。 例如,确保removeAllObjects不会崩溃是好的,但是如果另一个线程试图同时处理数组,它将在删除所有元素之前或之后处理数组,而且您必须想想应该是什么样的行为。
创build一个负责该数组的类+对象,为其创build一个串行队列,并通过该串行队列中的类执行所有操作是最简单的方法,使事情正确,而不会因为同步问题而让大脑受伤。
几乎NSMutable类对象不是线程安全的。
所有的NSMutablexxx类都不是线程安全的。 包括获取,插入,删除,添加和replace的操作应该与NSLock一起使用。这是由Apple给出的线程安全和线程不安全类的列表: 线程安全摘要