NSLock +原子属性vs非原子

时间:2013-07-08 03:37:00

标签: objective-c multithreading thread-safety locking foundation

我对目标C相当新。如果我有一个类属性可能会在API调用等异步事件期间被修改,那么确保在另一个属性访问它时更改属性的最佳方法是什么线程不会导致崩溃?

据我所知,我有两个选择:

1)NSLock +原子属性

...但似乎在这种情况下我必须为每次读取和写入锁定属性,这对我来说会失败将其设置为原子的目的。

2)非原子属性

我也可以将它设置为非原子的,但是我认为我必须在主线程上进行所有读/写操作。有没有办法通过API调用来做到这一点?成功的API响应之后,如果在为该API调用打开的线程上回调给委托,或者它是否会返回主线程?如果它在不同的线程上,我可以把它放回主线程吗?具体来说,我担心NSArray会在另一个线程循环播放时发生变化。

这样做的最佳方式是什么?

3 个答案:

答案 0 :(得分:4)

我想抓住justin的选项“dispatch APIs”作为一个简短的例子:

通过在专用串行队列上执行所有访问,可以使对共享资源的并发访问变得安全,我们称之为“sync_queue”。

这个“sync_queue”可能是该类的私有队列,其ivars是您要修改的资源。

您现在可以定义读/写非原子属性,例如:

@propery (nonatomic) NSArray* array;

write 访问权限可以如下所示实现:

- (void) setArray:(NSArray* newValue) 
{
    dispatch_async(sync_queue, ^{
        _array = newValue;
    });
}

请注意,写访问是异步的。

读取对该属性的访问权限将按如下方式实现:

- (NSArray*) array:(NSArray* value) 
{
    if (dispatch_get_specific(SyncQueueID) == sync_queue_id)) {
        return _array;
    }
    else {
        __block NSArray* result = nil;
        dispatch_sync(_sync_queue, ^{
            result = _array;
        });
        return result;
    }     
}

与写访问不同,读访问需要同步。该方法还必须检查当前执行上下文是否已经是sync_queue或同步队列的子项或任何子项 - 否则,读访问将导致死锁。

要识别当前执行上下文,我们将特定标识符与同步队列相关联,在创建时使用函数dispatch_queue_set_specific()。稍后我们使用dispatch_get_specific从当前队列或父父队列或任何祖父队列中获取此标识符。如果它返回此特定标识符,则该方法分别在子队列或任何大子节点上的同步队列上执行。如果这是真的,该方法立即返回值。否则,它会同步调度同步队列。

注意:

如果UIKit将访问共享资源,则sync_queue应为主队列。

答案 1 :(得分:2)

这更像是3个选项:

  1. NSLock
  2. 原子属性
  3. 非原子属性
  4. 1)是的,你必须为每次读写都锁定属性。这确实可以让您灵活地锁定整个迭代,而不仅仅是每次访问cellection。

    2)所有东西都只适用于访问变量(但是,你可以生成一个数组在迭代时被变异的情况,因为整个迭代没有锁定)

    3)是的,您可以通过回调主线程来完成所有读/写操作。是否在处理线程或主线程上调用委托方法取决于您使用的调用/您使用的框架。您可以使用GCD或执行选择器切换回主线程。

答案 2 :(得分:2)

  

如果我有一个类属性,可能会在API调用等异步事件期间被修改,那么确保在另一个线程访问属性时更改属性的最佳方法是什么?不会导致崩溃?

对于可变对象,您需要某种形式的互斥。根据抽象级别和用法,有很多选项。例如:

  • pthread_mutex* API
  • NSLock API
  • @synchronized
  • 信号量
  • 派遣API
  

1)NSLock +原子属性......但是在这种情况下我似乎必须为每次读取和写入锁定属性,这对我来说将失去将其设置为原子的目的。

完全。如果您需要锁定每次访问, Atomic不提供任何内容。原子实际上很有用是非常罕见的(一个属性如此简单并且独立于任何其他状态的极端情况)。

更详细地说,您已提到NSArray。如果那是copy属性(它应该是),那么在极少数情况下,atomic可以让你在实践中通过不可变副本安全地获取/设置数组。但是,在实践中,拥有一个只是指向不可变数组实例的指针的类并不是很有用;通常,您希望对该数组执行某些操作,并且通常希望以线程安全的方式与对象进行交互。隐含的含义也可以用于数组元素的互斥(如果正确完成)。

那么你需要在哪里锁定以保证NSMutableArray ivar的互斥?设置,获取时,几乎每次发送消息时。即使询问其-count或其元素,也应该锁定以消除任何竞争条件。当然,您可以将其包含在更高级别的操作中以确保正确性并执行这些操作 - 获取锁定一次。

  

2)非原子属性

Atomic不会拯救你,也不会拯救你。在这种情况下,Atomic只能为您节省一些潜在的竞争条件。因此,您通常应该使用非原子,因为您已经需要引入完全互斥以保证没有竞争条件。

  

成功的API响应后,在为该API调用打开的线程上回调给一个委托,还是会回到主线程上?

这取决于API。

  

如果它在不同的线程上,我可以把它放回主线程吗?

是的,你可以将它添加到主线程的运行循环中或使用调度队列。这是'kludgey',除非工作需要在特定的线程上发生 - 最明显的情况是更新AppKit或UIKit视图时。