在读取时变异数组,而不是枚举

时间:2012-08-12 07:51:36

标签: cocoa concurrency grand-central-dispatch

如果我有两个不同的线程通过GCD访问NSMutableArray而一个只是创建一个基于可变数组的新数组,而另一个线程正在从数组中删除记录,我应该期待这是一个问题?也就是说,我认为副本不应该仅仅是“读取”数组,只是得到当时阵列中发生的一切?我没有在任何一个线程中枚举数组,但它仍然崩溃。一旦我删除了读取例程,它就可以正常工作。

这是“阅读”:

  dispatch_async(saveQueue, ^{

    NSDictionary*tempstocks=[NSDictionary dictionaryWithDictionary:self.data];

它在这个帖子上崩溃:*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[9]'

以下是另一个主题上发生的事情:

[self.data removeObjectForKey:item];

我知道你在枚举时不能变异,但我认为在变异时读取是可以的,你可能不知道你得到了哪个版本的变异对象,但我不认为这是一个问题,但是显然它是。也许dictionaryWithDictionary方法正在执行首先看到X对象的操作,但是在例程完成时它包含XY对象,因此它不会在一次捕捉中“捕获”整个self.data字典。运行dictionaryWithDictionary而是枚举self.data,这与枚举时的变异问题基本相同?

3 个答案:

答案 0 :(得分:5)

我想您可以使用GCD创建三个不同的队列:一个用于保存,第二个用于其他内容,最后一个用于NSMutableArray

dispatch_async(saveQueue, ^{
    dispatch_barrier_async(_queue, ^{
            NSDictionary*tempstocks=[NSDictionary dictionaryWithDictionary:self.data];
        });
});

dispatch_async(anotherQueue, ^{
    dispatch_barrier_async(_queue, ^{
            [self.data removeObjectForKey:item];
        });
});

就像@synchronize但使用GCD。

更多信息:GCD Reference/dispatch_barrier_asynchttp://www.mikeash.com/pyblog/friday-qa-2011-10-14-whats-new-in-gcd.html

修改

我做了一些性能测试,以了解哪种方式更快:

- (void)usingSynchronized
{
    dispatch_queue_t writeQyeue = dispatch_queue_create("com.tikhop.writeQyeue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(writeQyeue, ^{
        for(size_t i=0; i<10000; i++)
            @synchronized (arr) {
                [arr replaceObjectAtIndex:0 withObject:[NSNumber numberWithInt:1]];
                [arr replaceObjectAtIndex:0 withObject:[NSNumber numberWithInt:2]];
                [arr replaceObjectAtIndex:0 withObject:[NSNumber numberWithInt:3]];
                [arr replaceObjectAtIndex:0 withObject:[NSNumber numberWithInt:4]];
            }
    });
}

- (void)usingGCD
{
    dispatch_queue_t writeQyeue = dispatch_queue_create("com.tikhop.writeQyeue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(writeQyeue, ^{
        for(size_t i=0; i<10000; i++)
            dispatch_barrier_async(_queue, ^{
                [arr replaceObjectAtIndex:0 withObject:[NSNumber numberWithInt:5]];
                [arr replaceObjectAtIndex:0 withObject:[NSNumber numberWithInt:6]];
                [arr replaceObjectAtIndex:0 withObject:[NSNumber numberWithInt:7]];
                [arr replaceObjectAtIndex:0 withObject:[NSNumber numberWithInt:8]];
            });
    });
}

arr = [NSMutableArray arrayWithCapacity:1];
[arr addObject:@(0)];

[self usingSynchronized];
[self usingGCD];

我得到以下结果: enter image description here

答案 1 :(得分:1)

您不能假设NSDictionary上的任何操作都是线程安全的。几乎所有这些都不是。您确实需要设置对阵列的互斥,@synchronize访问权限或使用gcd串行队列进行访问。

答案 2 :(得分:0)

dictionaryWithDictionary:在内部枚举参数,所以你在枚举时基本上是变异的。

此外,一般情况下,如果另一个线程要以任何方式访问它,除非使用某种同步原语,否则不应该写入对象。

你的理由是它“读取”目前的任何内容,一般无效。以下是关于多线程Usage of registers by the compiler in multithreaded program

中固有问题的更多信息