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

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

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

对于声明为atomic而且又自己手动实现getter或者setter的属性,也可以用barrier来改进:

  1. @property (atomic, copy) NSString *someString; 
  2.  
  3. - (NSString *)someString { 
  4.     __block NSString *tempString; 
  5.     dispatch_sync(_syncQueue, ^{ 
  6.         tempString = _someString; 
  7.     }); 
  8.     return tempString; 
  9.  
  10. - (void)setSomeString :(NSString *)someString { 
  11.     dispatch_barrier_async(_syncQueue, ^{ 
  12.         _someString = someString 
  13.         ... 
  14.     } 

在做到atomic的同时,getter之间还可以并发执行,比直接把setter和getter都放到串行队列或者加普通锁要更高效。

读者写者锁能提升多少效率?

使用读者写者锁一定比所有读写都加锁以及使用串行队列要快,但是到底能快多少呢?Dmytro Anokhin在[3]中做了实验对比,测出了分别使用NSLock、GCD barrier和pthread_rwlock时获取锁所需要的平均时间,实验样本数在100到1000之间,去掉最高和最低的10%,结果如下列图表所示:

iOS多线程开发:几个容易被忽略的细节
3 writers / 10 readers

iOS多线程开发:几个容易被忽略的细节
1 writer / 10 readers

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

iOS多线程开发:几个容易被忽略的细节
10 writers / 1 reader

分析可知:

  1. 使用读者写者锁(GCD barrier、pthread_rwlock),相比单纯使用普通锁(NSLock),效率有显著提升;
  2. 读者数量越多,写者数量越少,使用读者写者锁的效率优势越明显;
  3. 使用GCD barrier和使用pthread_rwlock的效率差异不大。

由于pthread_rwlock不易使用且容易出错,而且GCD barrier和pthread_rwlock对比性能相当,建议使用GCD barrier来解决iOS开发中遇到的读者写者问题。另外,使用GCD还有个潜在优势:GCD面向队列而非线程,dispatch至某一队列的任务,可能在任一线程上执行,这些对开发者是透明的,这样设计的好处显而易见,GCD可以根据实际情况从自己管理的线程池中挑选出开销最小的线程来执行任务,最大程度减小context切换次数。

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

何时使用读者写者锁

需要注意的是,并非所有的多线程读写场景都一定是读者写者问题,使用时要注意辨别。例如以下YYCache的代码:

  1. //读cache 
  2. - (id)objectForKey:(id)key { 
  3.     if (!key) return nil; 
  4.     pthread_mutex_lock(&_lock); 
  5.     _YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key)); 
  6.     if (node) { 
  7.         node->_time = CACurrentMediaTime(); 
  8.         [_lru bringNodeToHead:node]; 
  9.     } 
  10.     pthread_mutex_unlock(&_lock); 
  11.     return node ? node->_value : nil; 
  1. //写cache 
  2. - (void)setObject:(id)object forKey:(id)key withCost:(NSUInteger)cost { 
  3.     if (!key) return; 
  4.     if (!object) { 
  5.         [self removeObjectForKey:key]; 
  6.         return; 
  7.     } 
  8.     pthread_mutex_lock(&_lock); 
  9.     _YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key)); 
  10.     NSTimeInterval now = CACurrentMediaTime(); 
  11.     if (node) { 
  12.         _lru->_totalCost -= node->_cost; 
  13.         _lru->_totalCost += cost; 
  14.         node->_cost = cost; 
  15.         node->_time = now; 
  16.         node->_value = object; 
  17.         [_lru bringNodeToHead:node]; 
  18.     } else { 
  19.         node = [_YYLinkedMapNode new]; 
  20.         node->_cost = cost; 
  21.         node->_time = now; 
  22.         node->_key = key; 
  23.         node->_value = object; 
  24.         [_lru insertNodeAtHead:node]; 
  25.     } 
  26.     if (_lru->_totalCost > _costLimit) { 
  27.         dispatch_async(_queue, ^{ 
  28.             [self trimToCost:_costLimit]; 
  29.         }); 
  30.     } 
  31.     if (_lru->_totalCount > _countLimit) { 
  32.         _YYLinkedMapNode *node = [_lru removeTailNode]; 
  33.         if (_lru->_releaseAsynchronously) { 
  34.             dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue(); 
  35.             dispatch_async(queue, ^{ 
  36.                 [node class]; //hold and release in queue 
  37.             }); 
  38.         } else if (_lru->_releaseOnMainThread && !pthread_main_np()) { 
  39.             dispatch_async(dispatch_get_main_queue(), ^{ 
  40.                 [node class]; //hold and release in queue 
  41.             }); 
  42.         } 
  43.     } 
  44.     pthread_mutex_unlock(&_lock); 

(编辑:核心网)

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

热点阅读