ios Objective-C 的 NSMutableArray 线程安全吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12098011/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 19:51:24  来源:igfitidea点击:

Is Objective-C's NSMutableArray thread-safe?

objective-ciosmultithreadingnsmutablearray

提问by aryaxt

I've been trying to fix this crash for almost a week. The application crashes without any exception or stack-trace. The application does not crash in any way while running through instruments in zombie mode.

我一直试图修复这个崩溃将近一个星期。应用程序崩溃,没有任何异常或堆栈跟踪。在僵尸模式下运行仪器时,应用程序不会以任何方式崩溃。

I have a method that gets called on a different thread. The solution that fixed the crash was replacing

我有一个在不同线程上调用的方法。修复崩溃的解决方案是更换

[self.mutableArray removeAllObjects];

with

dispatch_async(dispatch_get_main_queue(), ^{
    [self.searchResult removeAllObjects];
});

I thought it might be a timing issue, so I tried to synchronize it, but it still crashed:

我以为可能是时间问题,所以我尝试同步它,但它仍然崩溃:

@synchronized(self)
{
    [self.searchResult removeAllObjects];
}

Here is the code

这是代码

- (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
        }
    }
}

Is this problem with NSMutableArray not being thread-safe?

NSMutableArray 的这个问题不是线程安全的吗?

回答by Daniel

No.

不。

It is not thread safe and if you need to modify your mutable array from another thread you should use NSLockto ensure everything goes as planned:

它不是线程安全的,如果您需要从另一个线程修改可变数组,您应该使用它NSLock来确保一切按计划进行:

NSLock *arrayLock = [[NSLock alloc] init];

[...] 

[arrayLock lock]; // NSMutableArray isn't thread-safe
[myMutableArray addObject:@"something"];
[myMutableArray removeObjectAtIndex:5];
[arrayLock unlock];

回答by Jingjie Zhan

As others already said, NSMutableArray is not thread safe. In case anyone want to achieve more than removeAllObject in a thread-safe environment, I will give another solution using GCD besides the one using lock. What you have to do is to synchronize the read/update(replace/remove) actions.

正如其他人已经说过的, NSMutableArray 不是线程安全的。如果有人想在线程安全的环境中实现更多的 removeAllObject,除了使用锁的解决方案之外,我将使用 GCD 提供另一种解决方案。您需要做的是同步读取/更新(替换/删除)操作。

First get the global concurrent queue:

首先获取全局并发队列:

dispatch_queue_t concurrent_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

For read:

供阅读:

- (id)objectAtIndex:(NSUInteger)index {
    __block id obj;
    dispatch_sync(self.concurrent_queue, ^{
        obj = [self.searchResult objectAtIndex:index];
    });
    return obj;
}

For insert:

对于插入:

- (void)insertObject:(id)obj atIndex:(NSUInteger)index {
    dispatch_barrier_async(self.concurrent_queue, ^{
        [self.searchResult insertObject:obj atIndex:index];
    });
}

From Apple Doc about dispatch_barrier_async:

从 Apple Doc 关于 dispatch_barrier_async :

When the barrier block reaches the front of a private concurrent queue, it is not executed immediately. Instead, the queue waits until its currently executing blocks finish executing. At that point, the barrier block executes by itself. Any blocks submitted after the barrier block are not executed until the barrier block completes.

当屏障块到达私有并发队列的前端时,它不会立即执行。相反,队列一直等到其当前正在执行的块完成执行。此时,屏障块会自行执行。在屏障块完成之前,不会执行在屏障块之后提交的任何块。

Similar for remove:

类似的删除:

- (void)removeObjectAtIndex:(NSUInteger)index {
    dispatch_barrier_async(self.concurrent_queue, ^{
        [self.searchResult removeObjectAtIndex:index];
    });
}

EDIT: Actually I found another simpler way today to synchronize access to a resource by using a serial queue provided by GCD.

编辑:实际上,我今天找到了另一种更简单的方法,通过使用 GCD 提供的串行队列来同步对资源的访问。

From Apple Doc Concurrency Programming Guide > Dispatch Queues:

从 Apple Doc Concurrency Programming Guide > Dispatch Queues

Serial queues are useful when you want your tasks to execute in a specific order. A serial queue executes only one task at a time and always pulls tasks from the head of the queue. You might use a serial queue instead of a lock to protect a shared resource or mutable data structure. Unlike a lock, a serial queue ensures that tasks are executed in a predictable order. And as long as you submit your tasks to a serial queue asynchronously, the queue can never deadlock.

当您希望任务以特定顺序执行时,串行队列很有用。串行队列一次只执行一个任务,并且总是从队列的头部拉取任务。您可以使用串行队列而不是锁来保护共享资源或可变数据结构。与锁不同,串行队列确保任务以可预测的顺序执行。而且只要您将任务异步提交到串行队列,队列就永远不会死锁。

Create your serial queue:

创建您的串行队列:

dispatch_queue_t myQueue = dispatch_queue_create("com.example.MyQueue", NULL);

Dispatch tasks async to the serial queue:

将任务异步分派到串行队列:

dispatch_async(myQueue, ^{
    obj = [self.searchResult objectAtIndex:index];
});

dispatch_async(myQueue, ^{
    [self.searchResult removeObjectAtIndex:index];
});

Hope it helps!

希望能帮助到你!

回答by Nathan Day

As well as NSLockcan also use @synchronized(condition-object) you just have to make sure every access of the array is wrapped in a @synchronizedwith the same object acting as the condition-object, if you only want to modify the contents of the same array instance then you can use the array itself as the condition-object, other wise you will have to use something else you know will not go away, the parent object, i.e self, is a good choice because it will always be the same one for the same array.

以及NSLock也可以使用@synchronized条件对象),如果您只想修改同一数组实例的内容,您只需确保数组的每次访问都包含在@synchronized条件对象相同的对象中那么你可以使用数组本身作为条件对象,否则你将不得不使用其他你知道不会消失的东西,父对象,即自我,是一个不错的选择,因为它对于相同的阵列。

atomic in @propertyattributes will only make setting the array thread safe not modifying the contents, i.e. self.mutableArray= ... is thread safe but [self.mutableArray removeObject:]is not.

@property属性中的atomic只会使设置数组线程安全而不修改内容,即self.mutableArray= ... 是线程安全但[self.mutableArray removeObject:]不是。

回答by sash

__weak typeof(self)weakSelf = self;

 @synchronized (weakSelf.mutableArray) {
     [weakSelf.mutableArray removeAllObjects];
 }

回答by gnasher729

Since serial queues were mentioned: With a mutable array, just asking "is it thread safe" isn't enough. For example, making sure that removeAllObjects doesn't crash is all good and fine, but if another thread tries to process the array at the same time, it will either process the array beforeor afterall elements are removed, and you really have to think what the behaviour should be.

由于提到了串行队列:对于可变数组,仅询问“它是否线程安全”是不够的。例如,确保 removeAllObjects 不会崩溃一切都很好,但是如果另一个线程尝试同时处理该数组,它将在删除所有元素之前之后处理该数组,您真的必须想想应该是什么行为。

Creating one class + object that is responsible for this array, creating a serial queue for it, and doing all operations through the class on that serial queue is the easiest way to get things right without making your brain hurt through synchronisation problems.

创建一个负责这个数组的类 + 对象,为它创建一个串行队列,并通过该串行队列上的类执行所有操作,这是最简单的方法,可以在不因同步问题而使您的大脑受伤的情况下把事情做好。

回答by holybiner

All the NSMutablexxx classes are not thread-safe. Operations including get,insert,remove,add and replace should be used with NSLock.This is a list of thread-safe and thread-unsafe classes given by apple: Thread Safety Summary

所有 NSMutablexxx 类都不是线程安全的。包括get、insert、remove、add和replace在内的操作都应该和NSLock一起使用。这是苹果给出的线程安全和线程不安全类的列表:线程安全总结

回答by Hardeep Singh

Almost NSMutable classes object is not thread safe.

几乎 NSMutable 类对象不是线程安全的。