我对目标C相当陌生。如果我有一个类属性可能在异步事件(如API调用)中被修改,那么确保在另一个线程访问属性时更改该属性不会导致崩溃的最佳方法是什么?
据我所知,我有两个选择:
1) NSLock +原子性质
在这种情况下,...but似乎需要锁定每次读写的属性,这将使我无法实现将其设置为原子的目的。
2)非原子性质
我也可以将它设置为非原子的,但我认为我必须在主线程上完成所有的读写操作。是否有一种方法作为API调用的结果?在成功的API响应之后对委托的调用是在为该API调用打开的线程上进行的,还是会回到主线程上?如果它在另一个线程上,我能把它放回主线程上吗?具体来说,我担心的是,当另一个线程正在循环时,NSArray会被更改。
做这件事最好的方法是什么?
发布于 2013-07-08 13:22:46
我想拿出justin的选项“分派API”作为一个简短的例子:
通过在专用串行队列上执行所有访问,可以使对共享资源的并发访问变得安全,让我们称之为"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或同步队列的子级或任何大子级-否则,读取访问将导致死锁。
为了标识当前的执行上下文,我们在创建同步队列时使用函数具体()将特定的标识符与同步队列关联起来。稍后,我们使用特定的从当前队列或父队列或任何大父队列获取此标识符。如果它返回此特定标识符,则该方法分别在子队列或任何大子队列上执行同步队列。如果为真,则该方法立即返回该值。否则,它将同步调度同步队列。
注意:
如果UIKit将访问共享资源,则sync_queue将成为主队列。
发布于 2013-07-08 06:32:20
它更像是三个选项:
1)是的,每次读和写,你都得锁上这个属性。这确实使您可以灵活地锁定整个迭代,而不仅仅是每次访问单元。
2)所有操作都只用于访问变量(但是,您可以生成这样一种情况,即数组在迭代时发生了变异,因为整个迭代过程中没有锁定)。
是的,你可以通过调用主线程来完成所有的读/写。委托方法是在处理线程上调用,还是在主线程上调用,取决于您进行的调用/使用的框架。您可以用GCD或执行选择器切换回主线程。
发布于 2013-07-08 08:48:32
如果我有一个类属性可能在异步事件(如API调用)期间被修改,那么确保在另一个线程访问属性时更改该属性不会导致崩溃的最佳方法是什么?
对于可变对象,需要某种形式的互斥。有许多选项,取决于抽象级别和使用。以下方面的例子:
pthread_mutex* APINSLock API@synchronized1) NSLock + ...but --在这种情况下,我必须为每一次读写锁定该属性,这将使我无法实现将其设置为原子属性的目的。
一点儿没错。如果您需要锁定每个访问权限,原子就什么都不提供了。这是非常罕见的,原子实际上是有用的(一个角落的情况下,一个属性是如此简单,也独立于任何其他状态)。
更详细的是,您已经提到了NSArray。如果这是一个copy属性(应该是),那么在罕见的情况下,原子可以允许您在实践中通过不可变的副本安全地获取/设置数组。但是,在实践中,拥有一个指向不可变数组实例的指针的类并不是非常有用的;通常,您希望对该数组执行一些操作,并且通常希望以线程安全的方式与对象交互。存在问题的锁的含义也可以用于对数组的元素进行互斥(如果正确的话)。
那么,您需要在哪里锁定以保证NSMutableArray ivar的互斥呢?当设置,什么时候得到,几乎每一次你发信息的时候。甚至要求它的-count或它的元素应该包含一个锁,以消除任何竞争条件。当然,为了正确起见,您可以在更高级别的操作中完成这些操作,并执行这些操作--获取锁一次。
2)非原子性质
原子不会拯救你,非原子也不会。在这种情况下,原子只会将您从一些潜在的竞争条件中拯救出来。因此,您通常应该使用非原子的,因为您已经需要引入完全的互斥,以确保没有种族条件。
在成功的API响应之后对委托的调用是在为该API调用打开的线程上进行的,还是会回到主线程上?
这取决于API。
如果它在另一个线程上,我能把它放回主线程上吗?
是的,您可以将它添加到主线程的run循环中,或者使用分派队列。这是“kludgey”,除非工作需要在特定线程上进行--最明显的情况是在更新AppKit或UIKit视图时。
https://stackoverflow.com/questions/17518846
复制相似问题