Objective-C中的__block关键字和线程安全问题

4

我想知道在方法的上下文中如何使对一个 __block 修饰的变量的访问线程安全。

例如:

__block NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];

for (int i=0; i<20; i++) {
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        [dictionary setObject:@"test" forKey:@"test"];
    }];
    [someConcurrentQueue addOperation:operation];
}

这里将操作添加到并发队列中,dictionary变量可能会同时被不同的线程访问。

这样安全吗?如果不安全,如何确保对dictionary的访问是安全的?


6
我认为这里不需要使用__block,因为你并没有更改dictionary所指向的对象,只是更改了对象内部的属性。至于并发问题,除了显而易见的解决方案——使用串行队列或将不安全的操作包装在另一个块中,并将其调度到单个串行队列中,我不确定是否还有其他解决方案。 - UIAdam
2个回答

6
正如UIAdam在他的评论中所说,__block 在这里对你没有帮助; 你正在改变字典,而不是给变量赋值。 变量将永远指向同一个字典。
实际上,__block 在这里可能会对你造成伤害,因为它意味着变量不会被块捕获。 如果你没有使用ARC,则这意味着字典不会被保留,该块可能很快向已经死亡的对象发送消息。 我不确定ARC是否更改了这一点。 无论如何,你应该从该变量中删除__block。如果没有其他原因,没有它的代码更清晰地表达了你的意图。
至于你的实际问题,关于线程安全性,这个代码是不安全的。 根据线程安全摘要,可变集合类不是线程安全的:你必须一次只能从一个线程发送消息到可变集合。 同步是一种方法;将队列的最大并发操作数设置为1是另一种方法。

3
在 ARC 模式下,dictionary 会被保留。这里有更多信息:http://www.mikeash.com/pyblog/friday-qa-2011-09-30-automatic-reference-counting.html(看“Blocks”部分)。 - ivanzoid

4

这段代码不是线程安全的,但与__block关键字无关,因为您只能读取而不能写入它。

使其线程安全最简单的方法是使用@synchronized关键字。

__block NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];

for (int i=0; i<20; i++) {
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        @synchronized(dictionary) {
            [dictionary setObject:@"test" forKey:@"test"];
        }
    }];
    [someConcurrentQueue addOperation:operation];
}

您可以使用NSLock或任何类型的锁。

如需更多信息,请阅读此文档


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接