iOS多线程读写安全

读写操作一般都是比较耗时的操作,为了保证读写安全,同时提升性能,一般采取“多读单写”模式.即对同一资源,同一时间,保证以下状态:

  • 只能有一个写操作
  • 或者多个读操作
  • 读、写两种操作不能同时进行
iOS大概有三种方式可以实现读写安全:
1. automic automic 用于保证属性setter(写)、getter(读)的原子性操作, 相当于在settergetter方法中添加了线程同步锁.
参考objc4的objc-accessors.mm源码
  • setter方法:
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy) { if (offset == 0) { object_setClass(self, newValue); return; }id oldValue; id *slot = (id*) ((char*)self + offset); if (copy) { newValue = https://www.it610.com/article/[newValue copyWithZone:nil]; } else if (mutableCopy) { newValue = [newValue mutableCopyWithZone:nil]; } else { if (*slot == newValue) return; newValue = objc_retain(newValue); }if (!atomic) { oldValue = *slot; *slot = newValue; } else { spinlock_t& slotlock = PropertyLocks[slot]; slotlock.lock(); oldValue = *slot; *slot = newValue; slotlock.unlock(); }objc_release(oldValue); }

  • getter方法:
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) { if (offset == 0) { return object_getClass(self); }// Retain release world id *slot = (id*) ((char*)self + offset); if (!atomic) return *slot; // Atomic retain release world spinlock_t& slotlock = PropertyLocks[slot]; slotlock.lock(); id value = https://www.it610.com/article/objc_retain(*slot); slotlock.unlock(); // for performance, we (safely) issue the autorelease OUTSIDE of the spinlock. return objc_autoreleaseReturnValue(value); }

note: automic 原子操作,保证setter和getter操作的原子性,内部通过加锁来实现
2.读写锁 iOS中pthread_rwlock(读写锁)刚好可以满足这种场景. 读写锁有两种模式,阻塞式(xxx_xxlock. 加锁不成功,本线程阻塞等待)和非阻塞式(xxx_tryxxlock. 加锁不成功,本函数失败返回)
等待锁的线程会进入休眠
//初始化锁 - 初始化完成后, 是非锁定状态 pthread_rwlock_t lock; //动态初始化 pthread_rwlock_init(&lock, NULL); //静态初始化 PTHREASD_RWLOCK_INITIALIZER//读-加锁 返回0表示成功 pthread_rwlock_rdlock(&lock); //读-尝试加锁 返回0表示成功 pthread_rwlock_tryrdlock(&lock); //写-加锁 返回0表示成功 pthread_rwlock_wrdlock(&lock); //写-尝试加锁 返回0表示成功 pthread_rwlock_trywrlock(&lock); //解锁 pthread_rwlock_unlock(&lock); //销毁 pthread_rwlock_destroy(&lock);

example:
#import @property (assign, nonatomic) pthread_rwlock_t lock; - (void)viewDidLoad { [super viewDidLoad]; // 初始化锁 pthread_rwlock_init(&_lock, NULL); dispatch_queue_t queue = dispatch_get_global_queue(0, 0); for (int i = 0; i < 10; i++) { dispatch_async(queue, ^{ [self read]; }); dispatch_async(queue, ^{ [self write]; }); } }- (void)read { pthread_rwlock_rdlock(&_lock); sleep(1); NSLog(@"%s", __func__); pthread_rwlock_unlock(&_lock); }- (void)write { pthread_rwlock_wrlock(&_lock); sleep(1); NSLog(@"%s", __func__); pthread_rwlock_unlock(&_lock); }- (void)dealloc { pthread_rwlock_destroy(&_lock); }

3.线程+栅栏
  • GCD
使用dispatch_barrier_async, 传入的队列必须是dispatch_queue_cretate创建的并发队列 (FMDB使用串行队列保证读写安全,但是读操作也是串行执行,似乎...)
example:
// 初始化队列 dispatch_queue_t queue = dispatch_queue_create("rw_queue", DISPATCH_QUEUE_CONCURRENT); // 读 dispatch_async(self.queue, ^{ [self read]; }); // 写 dispatch_barrier_async(self.queue, ^{ [self write]; });

  • NSOperationQueue
串行队列可以保证写操作的安全-串行执行,并发队列可以实现读操作的高效-并行执行.
一个NSOperationQueue可以满足这两种要求,只需切换maxConcurrentOperationCount即可切换这两种模式.
同时为了满足读写互斥,还需添加栅栏或依赖. 具体,NSOperationQueue iOS13开始添加了栅栏操作:- (void)addBarrierBlock:(void (^)(void))barrier API_AVAILABLE(macos(10.15), ios(13.0), tvos(13.0), watchos(6.0)); ,使用栅栏比较简单一些, iOS13以下系统需要通过添加依赖实现该功能.
example:
@interface DataModel : NSObject @property(nonatomic,copy)NSString *name; @property(nonatomic)NSInteger count; @end@implementation DataModel- (NSString *)description { return [NSString stringWithFormat:@"You Have %ld %@ !", (long)self.count, self.name]; } @end/*NSOperation+RW.h*/ typedef enum : NSUInteger { OperationUnKnow= 0, OperationRead= 1, OperationWrite= 2, OperationToRead= 3, OperationToWrite = 4, } OpereationFlag; @interface NSOperation (RW) @property(nonatomic)OpereationFlag flag; @end/*NSOperation+RW.m*/ static void *OperationReadWriteFlagKey = &OperationReadWriteFlagKey; @implementation NSOperation (RW) -(void)setFlag:(OpereationFlag)flag{ objc_setAssociatedObject(self, OperationReadWriteFlagKey, @(flag), OBJC_ASSOCIATION_COPY_NONATOMIC); }- (OpereationFlag)flag{ id flag = objc_getAssociatedObject(self, OperationReadWriteFlagKey); return (OpereationFlag)[flag integerValue]; }@end/*DataBaseHandler.m*/- (void)readDataWithQuery:(nonnull NSString *)query parameter:(id _Nullable)param result:(nonnull DBResultBlock)result { [self appendTaskWith:query parameter:param result:result read:YES]; }- (void)updateDataWithQuery:(nonnull NSString *)query parameter:(id _Nullable)param result:(nonnull DBResultBlock)result { [self appendTaskWith:query parameter:param result:result read:NO]; }-(void)appendTaskWith:(nonnull NSString *)query parameter:(id _Nullable)param result:(nonnull DBResultBlock)result read:(BOOL)read{ __weak typeof(self) weakSelf = self; NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ if (read) { sleep(2); }else{ if ([param isKindOfClass:[NSDictionary class]]) { [weakSelf.model setValuesForKeysWithDictionary:param]; } sleep(1); } result(weakSelf.model.description); }]; if (read) { operation.flag = OperationRead; }else{ operation.flag = OperationWrite; }NSLog(@"**********************"); static BOOL log = NO; NSOperation *lastOperation = [[self.readWriteQueue operations] lastObject]; if (@available(iOS 13.0, *)) { //使用栅栏操作比较简单 if (read) { if (lastOperation) { if (lastOperation.flag==OperationRead) { [self.readWriteQueue addOperation: operation]; }else{ [self.readWriteQueue addBarrierBlock:^{ weakSelf.readWriteQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount; }]; [self.readWriteQueue addOperation:operation]; } }else{ self.readWriteQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount; [self.readWriteQueue addOperation:operation]; }}else{ if (lastOperation) { if (lastOperation.flag==OperationWrite) { [self.readWriteQueue addOperation: operation]; }else{ [self.readWriteQueue addBarrierBlock:^{ weakSelf.readWriteQueue.maxConcurrentOperationCount = 1; }]; [self.readWriteQueue addOperation:operation]; } }else{ self.readWriteQueue.maxConcurrentOperationCount = 1; [self.readWriteQueue addOperation:operation]; } } }else{ /* 连续的读操都需要依赖前边最接近的to-read-barrier, to-read-barrier 依赖前边最后一个写操作写操作前后依赖, 第一个依赖前边最近的to-write-barrier, to-write-barrier 依赖前边最近的连续的读操作 **/if (read) { if (lastOperation) { if (lastOperation.flag==OperationRead) { NSOperation *operatonObj = nil; NSArray *operations = [self.readWriteQueue.operations copy]; NSInteger count = self.readWriteQueue.operations.count; for (int i = 0; i < count -1; i++) { NSOperation *obj = [operations objectAtIndex:count - 1 - i]; if (obj.flag==OperationToRead&&!obj.finished&&!obj.cancelled) { operatonObj = obj; break; } } if (operatonObj) { if (log) { NSLog(@"R依赖TR"); } [operation addDependency:operatonObj]; } if (log) { NSLog(@"添加了R"); } [self.readWriteQueue addOperation: operation]; }else if(lastOperation.flag==OperationWrite){ NSOperation *toReadBarrier = [NSBlockOperation blockOperationWithBlock:^{ }]; [toReadBarrier setCompletionBlock:^{ weakSelf.readWriteQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount; }]; toReadBarrier.flag = OperationToRead; [toReadBarrier addDependency:lastOperation]; [operation addDependency:toReadBarrier]; if (log) { NSLog(@"TR依赖W"); NSLog(@"R依赖TR"); NSLog(@"添加了TR"); NSLog(@"添加了R"); }[self.readWriteQueue addOperation:toReadBarrier]; [self.readWriteQueue addOperation:operation]; } }else{ if (log) { NSLog(@"添加了R"); }self.readWriteQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount; [self.readWriteQueue addOperation:operation]; } }else{ if (lastOperation) { if (lastOperation.flag==OperationWrite) { if (log) { NSLog(@"W依赖W"); NSLog(@"添加了W"); }[operation addDependency:lastOperation]; [self.readWriteQueue addOperation:operation]; }else if(lastOperation.flag==OperationRead){ NSOperation *toWriteBarrier = [NSBlockOperation blockOperationWithBlock:^{ //NSLog(@"toWriteBarrier"); }]; [toWriteBarrier setCompletionBlock:^{ weakSelf.readWriteQueue.maxConcurrentOperationCount = 1; }]; toWriteBarrier.flag = OperationToWrite; NSArray *operations = [self.readWriteQueue.operations copy]; NSInteger count = self.readWriteQueue.operations.count; for (int i = 0; i < count - 1; i++) { NSOperation *operatonObj = [operations objectAtIndex:count - 1 - i]; if (operatonObj.flag==OperationRead) { if (!operation.cancelled&&!operation.isFinished) { [toWriteBarrier addDependency:operatonObj]; if (log) { NSLog(@"TW依赖R"); } } }else{ break; } } if (log) { NSLog(@"W依赖TW"); NSLog(@"添加了TW"); NSLog(@"添加了W"); }[operation addDependency:toWriteBarrier]; [self.readWriteQueue addOperation:toWriteBarrier]; [self.readWriteQueue addOperation:operation]; } }else{ self.readWriteQueue.maxConcurrentOperationCount = 1; [self.readWriteQueue addOperation:operation]; if (log) { NSLog(@"添加了W"); } } } } }@end

测试:
/*in test place*/ -(void)testReadWriteSingleQueue{ id db = [DataBaseRWQueueHandler shareHandler]; id apple = @{@"name": @"Apple", @"count": @2}; id orange = @{@"name": @"Orange", @"count":@10}; id peach = @{@"name": @"Peach", @"count":@10}; id grape = @{@"name": @"Grape", @"count":@20}; BOOL log = YES; [db updateDataWithQuery:@"" parameter:apple result:^(id_Nonnull result) { if (log) {NSLog(@"write1: %@", result); } }]; [db readDataWithQuery:@"" parameter:nil result:^(id_Nonnull result) { if (log) {NSLog(@"read1: %@", result); } }]; [db updateDataWithQuery:@"" parameter:orange result:^(id_Nonnull result) { if (log) {NSLog(@"write2: %@", result); } }]; [db readDataWithQuery:@"" parameter:nil result:^(id_Nonnull result) { if (log) {NSLog(@"read2: %@", result); } }]; [db updateDataWithQuery:@"" parameter:peach result:^(id_Nonnull result) { if (log) {NSLog(@"write3: %@", result); } }]; [db updateDataWithQuery:@"" parameter:grape result:^(id_Nonnull result) { if (log) {NSLog(@"write4: %@", result); } }]; [db readDataWithQuery:@"" parameter:nil result:^(id_Nonnull result) { if (log) {NSLog(@"read3: %@", result); } }]; [db readDataWithQuery:@"" parameter:nil result:^(id_Nonnull result) { if (log) {NSLog(@"read4: %@", result); } }]; }/* 打印:2020-03-14 17:27:09.101971+0800 Basic_iOS[48718:1193248] write1: You Have 2 Apple ! 2020-03-14 17:27:11.103866+0800 Basic_iOS[48718:1193248] read1: You Have 2 Apple ! 2020-03-14 17:27:12.107587+0800 Basic_iOS[48718:1193248] write2: You Have 10 Orange ! 2020-03-14 17:27:14.109110+0800 Basic_iOS[48718:1193247] read2: You Have 10 Orange ! 2020-03-14 17:27:15.110935+0800 Basic_iOS[48718:1193247] write3: You Have 10 Peach ! 2020-03-14 17:27:16.111248+0800 Basic_iOS[48718:1193248] write4: You Have 20 Grape ! 2020-03-14 17:27:18.115692+0800 Basic_iOS[48718:1193247] read3: You Have 20 Grape ! 2020-03-14 17:27:18.115692+0800 Basic_iOS[48718:1193248] read4: You Have 20 Grape !*/

【iOS多线程读写安全】共同学习,欢迎指正...
参考文章:
https://www.jianshu.com/p/a459ea2e7fec

    推荐阅读