9

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

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

1) NSLock + 原子属性

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

2) 非原子性质

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

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

4

3 回答 3

4

我想以 justin 的选项“dispatch APIs”为例:

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

这个“sync_queue”很可能是一个类的私有队列,它的 ivars 是你想要修改的资源。

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

@propery (nonatomic) NSArray* array;

访问可以实现如下所示:

- (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 应为主队列。

于 2013-07-08T13:22:46.283 回答
2

它更像是3个选项:

  1. NSLock
  2. 原子性质
  3. 非原子性质

1)是的,您必须为每次读写锁定属性。这确实使您可以灵活地锁定整个迭代,而不仅仅是每次访问 cellection。

2)一切都只适用于访问变量(但是,您可能会产生一种情况,即数组在迭代时发生突变,因为整个迭代没有锁定)

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

于 2013-07-08T06:32:20.577 回答
2

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

对于可变对象,您将需要某种形式的互斥。有很多选项,具体取决于抽象级别和用法。示例:

  • pthread_mutex*蜜蜂
  • NSLock蜜蜂
  • @synchronized
  • 信号量
  • 调度 API

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

确切地。如果您需要锁定所有访问权限,Atomic 什么都不提供。atomic 很少有真正有用的情况(属性如此简单且独立于任何其他状态的极端情况)。

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

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

2) 非原子性质

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

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

这取决于 API。

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

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

于 2013-07-08T08:48:32.177 回答