我正在阅读 Apple docs围绕线程安全,我并不完全清楚什么(在实践中)真正构成了一个线程安全的类。为了帮助更好地理解这一点,需要对以下类做些什么才能使其成为线程安全的(以及为什么)?
#import "UnsafeQueue.h"
@interface UnsafeQueue()
@property (strong, nonatomic) NSMutableArray *data;
@end
@implementation UnsafeQueue
- (id)peek {
return [self.data firstObject];
}
- (NSUInteger)length {
return [self.data count];
}
- (void)enqueue:(id)datum {
[self.data addObject:datum];
}
// other methods omitted...
@end
是否会简单地创建一个 ivar NSLock,然后锁定/解锁周围 所有 与底层 NSMutableArray 的交互?
仅要求数组计数的长度方法是否也需要执行此操作?
最佳答案
使类线程安全的最简单和最好的方法是使其不可变。那么你不必处理任何这些。它只是工作。确实值得花时间考虑是否需要在多线程上实现可变性。
但是,如果不可变类给您的设计带来了重大问题,那么实现它的最佳方法通常是使用 GCD 而不是锁。 GCD 的开销要低得多,一般来说更容易正确。
在这种特殊情况下,我将按照以下方式实现它(未经测试,我已经在 Swift 中工作了一段时间,所以如果我省略了分号,请原谅我):
#import "SafeQueue.h"
@interface SafeQueue()
@property (strong, nonatomic) NSMutableArray *data;
@property (strong, nonatomic) dispatch_queue_t dataQueue;
@end
@implementation SafeQueue
- (instancetype)init {
if (self = [super init]) {
_dataQueue = dispatch_queue_create("SafeQueue.data", DISPATCH_QUEUE_CONCURRENT);
}
return self;
}
- (id)peek {
__block id result = nil;
dispatch_sync(self.dataQueue, ^{ result = [self.data firstObject] });
return result;
}
- (NSUInteger)length {
__block NSUInteger result = 0;
dispatch_sync(self.dataQueue, ^{ result = [self.data count] });
return result;
}
- (void)enqueue:(id)datum {
dispatch_barrier_async(self.dataQueue, ^{ [self.data addObject:datum] });
}
// other methods omitted...
@end
请注意对所有读取器使用 dispatch_sync
,对所有写入器使用 dispatch_barrier_async
。这就是通过允许并行读取器和独占写入器来将开销降至最低的方法。如果没有争用(这是正常情况),dispatch_sync
的开销比锁(NSLock
或 @synchronized
甚至pthreads 锁)。
参见 Migrating Away from Threads从 Apple 获得更多关于如何更好地处理 Cocoa 并发的建议。
关于ios - 在 iOS 中创建一个类 "thread safe",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28005734/