NSMutableArray 线程并发与 GCD
NSMutableArray Thread Concurrency with GCD
我在 "sharedStore" 模式单例中有一个 NSMutableArray。
公开地,它只能通过将其转换为 NSArray 的方法访问。在 class 内,它是
@property (nonatomic, copy) NSMutableArray *myItems;
这个数组永远不会在单例之外被操纵,但是 ViewControllers 发送单例消息来操纵这个控制器。其中一些消息清空数组,一些重新填充它,等等。
在一个方法调用中数组为空而在下一个方法调用中数组为空的情况结束后,我开始实现一些并发行为。
这是我目前所做的:
在单例的.m文件中,我有一个
@property (nonatomic, strong) dispatch_queue_t arrayAccessQueue;
在我的单例初始化程序中,它被创建为一个串行队列。然后,每个与改变该数组有关的方法都在 dispatch_sync
调用中执行,例如:
dispatch_sync(self.arrayAccessQueue, ^{
[_myItems removeAllObjects];
});
这让事情变得更好,并且让我的应用程序运行得更流畅。但是,除了修复上述一种奇怪的行为之外,我无法量化它。我也觉得自己对表面之下可能潜伏的任何问题一无所知。
这个模式对我来说很有意义,但我应该使用其他东西吗,比如 @synchronize
或 NSLock
或 NSOperationQueue
?这会回来咬我吗?
使用GCD是正确的选择。唯一的 "gotcha" 是您需要对该队列执行所有操作:添加、删除、插入等。
我还会提到您需要确保不使用并发队列。您应该使用串行队列,无论如何这是默认设置。
使用 dispatch_sync
就可以,只要包装所有数组读写并确保它是串行队列即可。
但是您可以通过允许并发读取来改进。为此,请在所有数组读取周围使用 dispatch_sync
并在所有数组写入周围使用 dispatch_barrier_sync
。并将队列设置为并发。
这样做可以确保一次只能进行一次写入,读取将被阻塞直到写入完成,而写入将等待所有当前读取完成。
并发使用 GCD 队列并为数组提供某种访问器,您可以通过在读取时使用 dispatch_sync 和在写入时使用 dispatch_barrier_async 来同步读取和写入。
- (id)methodToRead {
id __block obj = nil;
dispatch_sync(syncQueue, ^{
obj = <#read_Something#>;
});
return obj;
}
- (void) methodsForWriting:(id)obj {
dispatch_barrier_async(syncQueue, ^{
// write passing obj to something
});
}
这将保证在写入期间所有内容都被锁定,无法读取。
我在 "sharedStore" 模式单例中有一个 NSMutableArray。
公开地,它只能通过将其转换为 NSArray 的方法访问。在 class 内,它是
@property (nonatomic, copy) NSMutableArray *myItems;
这个数组永远不会在单例之外被操纵,但是 ViewControllers 发送单例消息来操纵这个控制器。其中一些消息清空数组,一些重新填充它,等等。
在一个方法调用中数组为空而在下一个方法调用中数组为空的情况结束后,我开始实现一些并发行为。
这是我目前所做的:
在单例的.m文件中,我有一个
@property (nonatomic, strong) dispatch_queue_t arrayAccessQueue;
在我的单例初始化程序中,它被创建为一个串行队列。然后,每个与改变该数组有关的方法都在 dispatch_sync
调用中执行,例如:
dispatch_sync(self.arrayAccessQueue, ^{
[_myItems removeAllObjects];
});
这让事情变得更好,并且让我的应用程序运行得更流畅。但是,除了修复上述一种奇怪的行为之外,我无法量化它。我也觉得自己对表面之下可能潜伏的任何问题一无所知。
这个模式对我来说很有意义,但我应该使用其他东西吗,比如 @synchronize
或 NSLock
或 NSOperationQueue
?这会回来咬我吗?
使用GCD是正确的选择。唯一的 "gotcha" 是您需要对该队列执行所有操作:添加、删除、插入等。
我还会提到您需要确保不使用并发队列。您应该使用串行队列,无论如何这是默认设置。
使用 dispatch_sync
就可以,只要包装所有数组读写并确保它是串行队列即可。
但是您可以通过允许并发读取来改进。为此,请在所有数组读取周围使用 dispatch_sync
并在所有数组写入周围使用 dispatch_barrier_sync
。并将队列设置为并发。
这样做可以确保一次只能进行一次写入,读取将被阻塞直到写入完成,而写入将等待所有当前读取完成。
并发使用 GCD 队列并为数组提供某种访问器,您可以通过在读取时使用 dispatch_sync 和在写入时使用 dispatch_barrier_async 来同步读取和写入。
- (id)methodToRead {
id __block obj = nil;
dispatch_sync(syncQueue, ^{
obj = <#read_Something#>;
});
return obj;
}
- (void) methodsForWriting:(id)obj {
dispatch_barrier_async(syncQueue, ^{
// write passing obj to something
});
}
这将保证在写入期间所有内容都被锁定,无法读取。