加入收藏 | 设为首页 | 会员中心 | 我要投稿 核心网 (https://www.hxwgxz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 业界 > 正文

iOS多线程开发:几个容易被忽略的细节

发布时间:2019-06-07 08:19:08 所属栏目:业界 来源:Eternal_Love
导读:一般情况下,iOS开发者只要会使用GCD、@synchronized、NSLock等几个简单的API,就可以应对大部分多线程开发了,不过这样是否真正做到了多线程安全,又是否真正充分利用了多线程的效率优势呢?看看以下几个容易被忽略的细节。 读者写者问题(Readers-writers

在iOS中,上述代码中的PV原语可以替换成GCD中的信号量API,dispatch_semaphore_t来实现,但是需要额外维护一个readerCount以及实现readerCount互斥访问的信号量,手动实现比较麻烦,封装成统一接口有一定难度。不过好在iOS开发中可以找到现成的读者写者锁:

pthread_rwlock_t

这是一个古老的C语言层面的函数,用法如下:

  1. // Initialization of lock, pthread_rwlock_t is a value type and must be declared as var in order to refer it later. Make sure not to copy it. 
  2. var lock = pthread_rwlock_t() 
  3. pthread_rwlock_init(&lock, nil) 
  4.  
  5. // Protecting read section: 
  6. pthread_rwlock_rdlock(&lock) 
  7. // Read shared resource 
  8. pthread_rwlock_unlock(&lock) 
  9.  
  10. // Protecting write section: 
  11. pthread_rwlock_wrlock(&lock) 
  12. // Write shared resource 
  13. pthread_rwlock_unlock(&lock) 
  14.  
  15. // Clean up 
  16. pthread_rwlock_destroy(&lock) 

接口简洁但是却不友好,需要注意pthread_rwlock_t是值类型,用=赋值会直接拷贝,不小心就会浪费内存,另外用完后还需要记得销毁,容易出错,有没有更高级更易用的API呢?

GCD barrier

dispatch_barrier_async / dispatch_barrier_sync并不是专门用来解决读者写者问题的,barrier主要用于以下场景:当执行某一任务A时,需要该队列上之前添加的所有操作都执行完,而之后添加进来的任务,需要等待任务A执行完毕才可以执行,从而达到将任务A隔离的目的,具体过程如下图所示:

iOS多线程开发:几个容易被忽略的细节

如果将barrier任务之前和之后的并发任务换为读操作,barrier任务本身换为写操作,就可以将dispatch_barrier_async / dispatch_barrier_sync当做读者写者锁来使用了,下面把文初的使用普通锁实现的cache代码,用dispatch_barrier_async重写,做下对比:

  1. //实现一个简单的cache(使用普通锁) 
  2. - (void)setCache:(id)cacheObject forKey:(NSString *)key { 
  3.     if (key.length == 0) { 
  4.         return; 
  5.     } 
  6.     [_cacheLock lock]; 
  7.     self.cacheDic[key] = cacheObject; 
  8.     ... 
  9.     [_cacheLock unlock]; 
  10.  
  11. - (id)cacheForKey:(NSString *key) { 
  12.     if (key.length == 0) { 
  13.         return nil; 
  14.     } 
  15.     [_cacheLock lock]; 
  16.     id cacheObject = self.cacheDic[key]; 
  17.     ... 
  18.     [_cacheLock unlock]; 
  19.     return cacheObject; 
  1. //实现一个简单的cache(使用读者写者锁) 
  2. static dispatch_queue_t queue = dispatch_queue_create("com.gfzq.testQueue", DISPATCH_QUEUE_CONCURRENT); 
  3.  
  4. - (void)setCache:(id)cacheObject forKey:(NSString *)key { 
  5.     if (key.length == 0) { 
  6.         return; 
  7.     } 
  8.     dispatch_barrier_async(queue, ^{ 
  9.         self.cacheDic[key] = cacheObject; 
  10.         ... 
  11.     }); 
  12.  
  13. - (id)cacheForKey:(NSString *key) { 
  14.     if (key.length == 0) { 
  15.         return nil; 
  16.     } 
  17.     __block id cacheObject = nil; 
  18.     dispatch_sync(queue, ^{ 
  19.         cacheObject = self.cacheDic[key]; 
  20.         ... 
  21.     }); 
  22.     return cacheObject; 

这样实现的cache就可以并发执行读操作,同时又有效地隔离了写操作,兼顾了安全和效率。

(编辑:核心网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读