关于C#:NSLock +原子属性vs非原子

NSLock + atomic property vs nonatomic

我对目标C比较陌生。如果我有一个类属性,在异步事件(如API调用)中可能会被修改,那么最好的方法是什么,以确保在另一个线程访问该属性时更改它不会导致崩溃?

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

1)nslock+原子属性

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

2)非原子性

我也可以把它设置为非原子的,但是我想我必须在主线程上进行所有的读/写。作为API调用的结果,有没有一种方法可以做到这一点?成功的API响应之后对委托的回调是在为该API调用打开的线程上,还是在主线程上?如果它在另一条线上,我可以把它放回主线上吗?具体地说,我担心当另一个线程循环通过时,NSARRAY会发生变化。

最好的方法是什么?


我想抓住Justin的选项"调度API"作为一个简短的示例:

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

这个"同步队列"很可能是一个类的私有队列,该类的ivars是您要修改的资源。

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

@propery (nonatomic) NSArray* array;

写访问可以实现如下:

1
2
3
4
5
6
- (void) setArray:(NSArray* newValue)
{
    dispatch_async(sync_queue, ^{
        _array = newValue;
    });
}

注意,写访问是异步的。

对属性的读取访问将实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
- (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;
    }    
}

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

为了识别当前的执行上下文,我们在创建它时,使用函数DexChyQueLeEngStEuthAuthor()将特定标识符与同步队列关联起来。之后,我们使用DeXCHYGETGOIL指定从当前队列或从父或任何父队列获取此标识符。如果返回该特定标识符,则该方法分别在子队列或任何宏子孙上在同步队列上执行。如果这是真的,该方法立即返回值。否则,同步队列上同步调度。

注:

如果uikit将访问共享资源,则同步队列应为主队列。


If I have a class property that will likely be getting modified during asynchronous events like API calls, what is the best way to make sure that changing the property while it is being accessed by another thread will not cause a crash?

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

  • pthread_mutex*原料药
  • NSLock原料药
  • @synchronized
  • 信号量
  • 调度API

1) NSLock + atomic property ...but it seems like in this case I would have to lock the property for every read and write, which to me would defeat the purpose of setting it as atomic.

确切地。如果您需要锁定每个访问,Atomic将不提供任何内容。原子实际上是有用的,这是非常罕见的(一个角落的情况下,一个属性是如此简单,也独立于任何其他状态)。

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

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

2) Nonatomic property

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

Would a call back to a delegate after a successful API response be on the thread opened for that API call, or would it be back on the main thread?

这取决于API。

And if it's on a different thread, could I put it back on the main thread?

是的,您可以将它添加到主线程的运行循环中,或者使用调度队列。这是"kludgey",除非工作需要在特定线程上进行——最明显的情况是在更新appkit或uikit视图时。


它更像是3个选项:

  • 诺斯洛克
  • 原子性质
  • 非原子性
  • 1)是的,每次读写都必须锁定属性。这确实为您提供了锁定整个迭代的灵活性,而不仅仅是对单元部分的每个访问。

    2)所有东西都只对变量进行访问(但是,您可以生成这样一种情况:数组在迭代时发生了变化,因为整个迭代过程中没有锁)

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