Objective c NSLock&x2B;原子性质与非原子性质

Objective c NSLock&x2B;原子性质与非原子性质,objective-c,multithreading,thread-safety,locking,foundation,Objective C,Multithreading,Thread Safety,Locking,Foundation,我对objective C相当陌生。如果我有一个类属性,在API调用等异步事件期间可能会被修改,那么什么是确保在其他线程访问该属性时更改该属性不会导致崩溃的最佳方法 据我所知,我有两个选择: 1)NSLock+原子属性 …但在这种情况下,我似乎必须为每次读写锁定属性,对我来说,这将破坏将其设置为原子的目的 2)非原子性质 我也可以将它设置为非原子,但是我想我必须在主线程上进行所有的读/写操作。作为API调用的结果,有没有一种方法可以做到这一点?成功的API响应后对委托的回调是在为该API调用打开

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

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

1)NSLock+原子属性

…但在这种情况下,我似乎必须为每次读写锁定属性,对我来说,这将破坏将其设置为原子的目的

2)非原子性质

我也可以将它设置为非原子,但是我想我必须在主线程上进行所有的读/写操作。作为API调用的结果,有没有一种方法可以做到这一点?成功的API响应后对委托的回调是在为该API调用打开的线程上,还是在主线程上?如果它在不同的线程上,我可以把它放回主线程吗?具体地说,我担心一个NSArray在另一个线程循环时被更改


最好的方法是什么?

它更像是3个选项:

  • NSLock
  • 原子性质
  • 非原子性质
  • 1) 是的,您必须为每次读写锁定属性。这使您能够灵活地锁定整个迭代,而不仅仅是对CellSection的每次访问

    2) 一切都只是为了访问变量而工作(但是,您可以生成一种情况,即数组在迭代时发生了变化,因为整个迭代没有锁)

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

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

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

    • pthread\u mutex*
      api
    • NSLock
      api
    • @synchronized
    • 信号量
    • 调度API
    1) NSLock+原子属性…但在这种情况下,我似乎必须为每次读写锁定该属性,这对我来说会破坏将其设置为原子的目的

    没错。如果需要锁定每个访问,Atomic不提供任何功能。原子实际上是有用的,这是非常罕见的(在一个角落的情况下,一个属性是如此简单,并且独立于任何其他状态)

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

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

    2) 非原子性质

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

    成功的API响应后对委托的回调是在为该API调用打开的线程上,还是在主线程上

    这取决于API

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


    是的,您可以将其添加到主线程的运行循环或使用调度队列。除非工作需要在一个特定的线程上进行,否则这是“胡扯”——最明显的情况是在更新AppKit或UIKit视图时。

    我想用justin的选项“dispatch API”作为一个简短的例子:

    通过在专用串行队列上执行所有访问,可以确保对共享资源的并发访问安全,我们称之为“同步队列”

    这个“sync_队列”可能是一个类的私有队列,该类的ivar是您要修改的资源

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

    @propery(非原子)NSArray*数组

    写访问可以实现如下所示:

    - (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_队列或sync队列的子级或任何子级-否则,读取访问将导致死锁

    为了识别当前执行上下文,我们在创建同步队列时使用函数将特定标识符与同步队列相关联。稍后,我们使用从当前队列、父队列或任何父队列获取该标识符。如果返回此特定标识符,则该方法将分别在同步队列上的