一、读写锁

在多线程环境下,我们经常会遇到读写同步的问题。常见的做法就是将读写做成互斥操作,但是这样会降低读写性能。通常情况下我们希望读操作之间是不互斥的,这个时候就需要用到读写锁了。简单来说,读写锁要做的就是保证:

  • 读与读不互斥
  • 读与写互斥
  • 写与写互斥

二、GCD提供的读写锁

GCD的并发队列有一个Barrier Block的概念,关于Barrier Block,Apple给出的解释如下:

  • Will not run until all blocks submitted earlier have completed
  • Blocks submitted later will not run until barrier block has completed

Barrier Block的这种同步机制给我们提供了一个很好的途径来解决临界资源的读写问题。将读写操作放入一个并发队列,同时写操作放入Barrier Block中,就能实现一个读写锁的功能了。由于写操作是一个Barrier Block,它会等待之前的所有读操作结束后才能执行,在写操作之后 的读操作也需要等待写操作结束后才能执行,以下是Apple给出的官方写法:

- (id)objectAtIndex:(NSUInteger)index
{__block id obj;dispatch_sync(self.concurrent_queue,^{obj = [self.array objectAtIndex:index];});return obj;
}- (void)insertObject:(id)obj atIndex:(NSUInteger)index
{dispatch_barrier_async(self.concurrent_queue,^{[self.array insertObject:obj atIndex:index];});
}

详情可以参考Apple在WWDC 2011上关于GCD的讲解内容:WWDC 2011 - Session 210 - iOS, OS X。可以直接跳到7:30处开始观看。 然而在实际使用过程中,上述形式在并发量很大的时候,会大概率遇到死锁,下面给出测试代码:

//  MultiThreadCache.m
//  MDHomeProject
//
//  Created by Leon on 2019/11/26.
//#import "MDMultiThreadCache.h"@interface MDMultiThreadCache ()
@property (nonatomic, strong) NSMutableDictionary *data;
@property (nonatomic, strong) dispatch_queue_t concurrentQueue;
@end@implementation MDMultiThreadCache- (instancetype)init
{self = [super init];if (self) {_data = [NSMutableDictionary dictionary];_concurrentQueue = dispatch_queue_create("MultiThreadCacheRWQueue",DISPATCH_QUEUE_CONCURRENT);}return self;
}- (id)objectForKey:(NSString *)key
{__block id obj = nil;dispatch_sync(self.concurrentQueue, ^{NSLog(@"读操作来了。。。");obj = [self.data objectForKey:key];});return obj;
}- (void)setObject:(id)object ForKey:(NSString *)key
{dispatch_barrier_async(self.concurrentQueue, ^{NSLog(@"写操作来了。。。");[self.data setObject:object forKey:key];});
}@end

调用处代码:

- (void)gcdTest
{dispatch_queue_t testQueue = dispatch_queue_create([@"MyGCDTest" UTF8String], DISPATCH_QUEUE_CONCURRENT);for (NSInteger index = 0; index < TIMES; index++) {if (index%10 == 9) {dispatch_async(testQueue, ^{[self.cache setObject:@(index) ForKey:[@(index) stringValue]];});} else {dispatch_async(testQueue, ^{[self.cache objectForKey:[@(index) stringValue]];});}}
}

我们发现大量的线程都在读操作的地方被阻塞了,并且写操作一次都没有执行过,如图:

我们尝试减小TIMES的值,将并发数给降下来。结果发现TIMES在70左右的时候,基本就不会发生死锁了。同时,在这个过程中,我们发现 一个很有趣的现象,每次发生死锁时,系统调用栈中都存在“66个线程”。如图:

这个现象给了我们一个思路,推测是系统的线程资源被消耗完了,导致写操作无法执行,后续的读操作也因为需要等待写操作的
结束,进而发生了死锁。通过分析之前的调用栈可以发现,”66个线程中”有64个都被测试队列“MyGCDTest”所占有(另外两个线程属于系统线程),通过简单的计算可以 得出,如果写操作在真正执行前,这“64个线程”都被读操作占用了,那么写操作就会因为却少线程资源而无法执行,进而发生死锁。

在实际的几次测试中,我们发现,针对TIMES ,我们无法找到一个准确的临界值来让死锁一定产生或一定不产生。因为GCD队列嵌套的原因,我们无法保证读写操作的准确先后顺序。但是,一个比较模糊的临界值(70左右)还是存在的。

通过这一轮的测试,我们可以初步得出一个结论:通过GCD Barrier来保证读写同步在一定程度上是可行的,但在并发量很大的情况,会因为线程资源消耗而导致死锁,需要根据实际情况来选择性使用。

iOS开发笔记之七十九——读写线程锁介绍(一)相关推荐

  1. iOS开发笔记之五十九——OS X和iOS安全机制之一——签名机制

    一.背景 在OS X上病毒和恶意软件很少见,给大家的感觉是OS X和iOS更加安全.到底是什么原因导致的OS X更加安全呢? (1)OS X占有率较低,没有吸引足够的恶意软件开发者的注意(假设你是恶意 ...

  2. SAP UI5 应用开发教程之七十九 - 采用测试驱动开发理念(Test Driven Development)进行 SAP UI5 应用的功能开发(一)的试读版

    一套适合 SAP UI5 初学者循序渐进的学习教程 教程目录 SAP UI5 本地开发环境的搭建 SAP UI5 应用开发教程之一:Hello World SAP UI5 应用开发教程之二:SAP U ...

  3. 【Visual C++】游戏开发笔记三十九 浅墨DirectX教程之七 他山之石:几种几何体的快捷绘制法

    本篇文章里,我们对Direct3D之中几种几何体的简洁绘制方法进行了详细的剖析,最后依旧是提供文章配套的详细注释的demo源代码的欣赏,并在文章末尾提供了源代码下载.(这标题有些歧义的,这个几种是修饰 ...

  4. Android开发笔记(十九)底部标签栏TabBar

    底部标签页实现思路 现在的APP,大多在页面底部显示标签栏Tabbar,用于切换不同栏目的页面.Tabbar起源于iOS,iOS的Tabbar自动位于页面下方,可是Android搬过来的时候做了改动, ...

  5. 校友会小程序开发笔记二十九:小程序启动性能评测与优化(2)

    校友会小程序定位是大量校友的社交类应用,因此对于性能,用户体验,交互体验要求很高,对于小程序的打开,流畅性, 数据的渲染都必须做尽可能的优化 运行时性能 setData setData 是校友会小程序 ...

  6. .Net开发笔记(十九) 创建一个可以可视化设计的对象

    阅读本篇博客之前需要了解VS窗体设计器的工作原理,详细可参见本系列博客(十).(十一).(十二).必须需要知道的一条结论就是:处于窗体设计器(Form Designer)中的任何组件(包含控件,下同) ...

  7. android开发标签栏应该设置多少,Android开发笔记(十九)底部标签栏TabBar

    底部标签页实现思路 现在的APP,大多在页面底部显示标签栏Tabbar,用于切换不同栏目的页面.Tabbar起源于iOS,iOS的Tabbar自动位于页面下方,可是Android搬过来的时候做了改动, ...

  8. OSG开发笔记(十九):OSG文字显示

    若该文为原创文章,未经允许不得转载 本文章博客地址:https://blog.csdn.net/qq21497936/article/details/97262841 各位读者,知识无穷而人力有穷,要 ...

  9. iOS开发笔记之二十二——归档Archiver(二)

    1.前言 曾学习过关于归档的基本知识,详见前期的<IOS学习笔记之十一>.本次重点介绍利用NSKeyedArchiver进行归档,设计一个可以对tableView的cell数据元素进行缓存 ...

最新文章

  1. java数据结构 队列_Java数据结构与算法[原创]——队列
  2. Vertical Menu ver4
  3. 记录 之 离线安装docker
  4. zigbee bind, ZDO_RegisterForZDOMsg, zcl_registerForMsg
  5. pythonrequests说明_解决Python requests 报错方法集锦
  6. 2018百度之星程序设计大赛 - 资格赛 1002 子串查询
  7. 暑期训练日志----2018.8.17
  8. PTA c语言 冒泡法排序
  9. devops_将DevOps带到教室
  10. 【渝粤教育】21秋期末考试市场营销10256k2
  11. WCDMA中的基本概念
  12. 怎么把文件传到虚拟机里
  13. C++中strncpy函数和strncpy_s函数的使用及注意事项
  14. 点云配准(PCL+ICP)
  15. MacTips-MacTalk
  16. Python+FFmpeg音视频格式转换
  17. 进阶实验5-3.2 新浪微博热门话题 (字符串操作)
  18. python的十句名言_程序员的二十句励志名言,看看你最喜欢哪句?
  19. springboot(十六) 2.0变化
  20. 一个强化学习 Q-learning 算法的简明教程

热门文章

  1. “鲁迅说过的话”检索系统上线就查询崩溃
  2. 企业级Oracle入门Linux/Unix基础①
  3. 如何z知道自己计算机名称,怎么查看自己电脑的型号
  4. 高德地图添加图片覆盖物,限制拖拽缩放范围
  5. 从作用域到作用域链,思维脑图+代码示例让知识点一目了然!系列(三)
  6. Java比较器-Comparable和Comparator
  7. 测试你像谁软件,2020你活的最像谁
  8. IntelliJ家族默认快捷键
  9. 联想预装Win10/Win8换Win7 教程 以及svn使用教程
  10. Taro框架开发小程序时不让首部的标题显示和更改默认头部背景