想法来自于项目中关于 Mutable Model 操作中的各种 Crash。跟同事交流后,最终我们的分歧落在了使用何种队列来解决问题上。
方案 1 采用了串行队列,多线程读时会互相等待;方案 2 采用了并发队列,有死锁的潜在风险,以下是苹果关于 dispatch_queue_create
的一段注释
Dispatch queues created with the DISPATCH_QUEUE_CONCURRENT attribute may
invoke blocks concurrently (similarly to the global concurrent queues, but
potentially with more overhead), and support barrier blocks submitted with
the dispatch barrier API, which e.g. enables the implementation of efficient
reader-writer schemes.
方案 1:
NS_ASSUME_NONNULL_BEGIN
@interface WBMutableArraySerialQueue ()
{
dispatch_queue_t _queue;
NSMutableArray *_array;
}
@end
@implementation WBMutableArraySerialQueue
- (instancetype)initWithArray:(NSArray *)array
{
self = [super init];
if (self)
{
_queue = dispatch_queue_create([[NSString stringWithFormat:@"WBMutableArrayQueue.%@", self] UTF8String], DISPATCH_QUEUE_SERIAL);
_array = [NSMutableArray arrayWithArray:array];
}
return self;
}
- (instancetype)init
{
self = [self initWithArray:@[]];
if (self)
{
}
return self;
}
- (void)inQueue:(__attribute__((noescape)) void (^)(NSMutableArray *array))block
{
dispatch_sync(_queue, ^() {
block(self->_array);
});
}
@end
NS_ASSUME_NONNULL_END
方案 2:
NS_ASSUME_NONNULL_BEGIN
@interface WBMutableArrayConcurrentQueue ()
{
dispatch_queue_t _queue;
NSMutableArray *_array;
}
@end
@implementation WBMutableArrayConcurrentQueue
- (instancetype)initWithArray:(NSArray *)array
{
self = [super init];
if (self)
{
_queue = dispatch_queue_create([[NSString stringWithFormat:@"WBMutableArrayQueue.%@", self] UTF8String], DISPATCH_QUEUE_CONCURRENT);
_array = [NSMutableArray arrayWithArray:array];
}
return self;
}
- (instancetype)init
{
self = [self initWithArray:@[]];
if (self)
{
}
return self;
}
- (void)inImmutableQueue:(__attribute__((noescape)) void (^)(NSArray *array))block
{
dispatch_sync(_queue, ^{
block((NSArray *)self->_array);
});
}
- (void)inOperationQueue:(__attribute__((noescape)) void (^)(NSMutableArray *array))block
{
dispatch_barrier_async(_queue, ^{
block(self->_array);
});
}
@end
NS_ASSUME_NONNULL_END
笔者目前的考虑是,既然选择了多线程读,那么在子线程中,对于“等待”的不耐就是低的。方案 1 串行队列开销小,没有死锁风险,是目前最优的折衷方案。
如果你有不同的意见,欢迎留言。
另外,YYThreadSafeArray 使用了锁包装了所有 MutableArray 的方法,这是一个崭新的思路。名义上是继承 NSMutableArray,实际上内部是组合了一个 NSMutableArray。一个可能的原因是他可能懒得在声明一遍那些本应该属于 NSMutableArray 的方法。
YYThreadSafeArray 的插入效率测试不如 Queue,但是拜父类继承所致,实际解决 bug 时,因为方法完全一致,排查 Crash 替换成本较低。
WBMutableArrayQueue vs YYThreadSafeArray
网友评论