背景

主要还是学习做笔记,并且锻炼下写文章能力,以及挽回我的flag T T,如有错,请评论指正

ibireme大佬在iOS 保持界面流畅的技巧 原文:

使用 concurrent queue 时不可避免会遇到这种问题,但使用 serial queue 又不能充分利用多核 CPU 的资源。我写了一个简单的工具 YYDispatchQueuePool,为不同优先级创建和 CPU 数量相同的 serial queue,每次从 pool 中获取 queue 时,会轮询返回其中一个 queue。我把 App 内所有异步操作,包括图像解码、对象释放、异步绘制等,都按优先级不同放入了全局的 serial queue 中执行,这样尽量避免了过多线程导致的性能问题。

作为YYKit的其中一个组件,也可以说相当精简,因此学习一下 在并发编程过程中会遇到两种任务:

  • CPU密集型task
  • IO密集型task

一般情况下,都是IO密集型的比较多,因此都会产生比较多的CPU空闲时间,而且现在多核的CPU可以实现并行的任务调度,如果不使用起来就会很浪费。

那这里为什么会提及的CPU密集型的,就是如果像上面提及的,在并发的过程中,创建过多的线程,导致线程的处理逼近CPU的处理的话,还是会导致性能下降。CPU通过时间片轮转等调度算法,实现伪并行(多核下还是能实现真并行)。

因此作者提出,通过自行控制queue的数量,将线程数控制到一个合理的数量(这里的合理数量就是,每个优先级的线程数保持和CPU核心数相同)

调用方式

首先先看调用方式简单了解

YYDispatchQueuePool *pool = [YYDispatchQueuePool defaultPoolForQOS:NSQualityOfServiceUtility];
dispatch_queue_t queue = [pool queue];
// 使用 dispatch_queue_t
dispatch_queue_t queue = [pool queue];
dispatch_async(queue, ^{NSLog(@"测试");
});
复制代码

自定义创建

/**创建一个dispatch_queuet_t的pool对象@param name       队列池的名字@param queueCount 队列的数量控制,最多32@param qos        队列的优先级*/
- (instancetype)initWithName:(NSString *)name queueCount:(NSUInteger)queueCountqos:(NSQualityOfService)qos;
复制代码

只通过优先级获取(推荐使用)

/**获取默认创建的pool对象@param qos        队列的优先级*/
+ (instancetype)defaultPoolForQOS:(NSQualityOfService)qos;
复制代码

为什么会推荐这个呢?这里主要就是已经帮你创建好对应的线程控制,如果你没什么特殊需求,直接使用即可。

实现细节

QoS(Quality of Service,服务质量)

用来代表服务的优先级

iOS8之前,优先级各有各的方式

NSOperation 和 NSThread 都通过 threadPriority 来指定优先级

GCD也只能设置

  • DISPATCH_QUEUE_PRIORITY_HIGH
  • DISPATCH_QUEUE_PRIORITY_LOW
  • DISPATCH_QUEUE_PRIORITY_BACKGROUND
  • DISPATCH_QUEUE_PRIORITY_DEFAULT

在iOS8之后 统一为5个等级

  • NSQualityOfServiceUserInteractive:最高优先级, 用于处理 UI 相关的任务
  • NSQualityOfServiceUserInitiated:次高优先级, 用于执行需要立即返回的任务
  • NSQualityOfServiceUtility:普通优先级,主要用于不需要立即返回的任务
  • NSQualityOfServiceBackground:后台优先级,用于处理一些用户不会感知的任务
  • NSQualityOfServiceDefault:默认优先级,当没有设置优先级的时候,线程默认优先级

GCD的则提供对应的

  • QOS_CLASS_USER_INTERACTIVE
  • QOS_CLASS_USER_INITIATED
  • QOS_CLASS_UTILITY
  • QOS_CLASS_BACKGROUND
  • QOS_CLASS_DEFAULT
  • QOS_CLASS_UNSPECIFIED

创建Context

Context一个结构体,用于记录上下文数据

typedef struct {const char *name;void **queues;uint32_t queueCount;int32_t counter; // 计数器
} YYDispatchContext;
复制代码

需要关注的是计数器,这个计数器是干嘛的?

一个context有n个queue,那我每次返回哪一个呢?

这里的逻辑是:

我轮着返回给你啊~

// 这个[pool queue],就是你主要使用的方法,获取一个可以用的队列防止任务
- (dispatch_queue_t)queue {return YYDispatchContextGetQueue(_context);
}static dispatch_queue_t YYDispatchContextGetQueue(YYDispatchContext *context) {// 计算器++ (调用一次,我就拿下一个~)uint32_t counter = (uint32_t)OSAtomicIncrement32(&context->counter);// 取出队列void *queue = context->queues[counter % context->queueCount];// 返回队列return (__bridge dispatch_queue_t)(queue);
}
复制代码

Context创建代码注释

static YYDispatchContext *YYDispatchContextCreate(const char *name,uint32_t queueCount,NSQualityOfService qos) {// 创建上下文,用于记录YYDispatchContext *context = calloc(1, sizeof(YYDispatchContext));if (!context) return NULL;// 创建指针数组context->queues =  calloc(queueCount, sizeof(void *));if (!context->queues) {free(context);return NULL;}if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {// 这个方法设置Qos只在8.0之后进行统一,不过还是需要转换一下枚举 dispatch_qos_class_t qosClass = NSQualityOfServiceToQOSClass(qos);// 根据队列的数量进行创建for (NSUInteger i = 0; i < queueCount; i++) {dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, qosClass, 0);dispatch_queue_t queue = dispatch_queue_create(name, attr);context->queues[i] = (__bridge_retained void *)(queue);}} else {// 获取Qos和GCD的对应关系long identifier = NSQualityOfServiceToDispatchPriority(qos);for (NSUInteger i = 0; i < queueCount; i++) {// 通过和全局队列的优先级设置对应的方式来设置dispatch_queue_t queue = dispatch_queue_create(name, DISPATCH_QUEUE_SERIAL);// queue的优先级和dispatch_get_global_queue(identifier, 0)优先级一致dispatch_set_target_queue(queue, dispatch_get_global_queue(identifier, 0));context->queues[i] = (__bridge_retained void *)(queue);}}context->queueCount = queueCount;if (name) {context->name = strdup(name);}return context;
}复制代码

通过Qos进行获取queue

为什么推荐通过Qos直接获取队列?当然是因为提供的便利控制数量的方法

通过 YYDispatchContextGetForQOS 方法去获取一个文

+ (instancetype)defaultPoolForQOS:(NSQualityOfService)qos {// 创建数量为5的context数组,为啥是5?因为有5个优先级~// 一人一个坑static YYDispatchContext *context[5] = {0};switch (qos) {case NSQualityOfServiceUserInteractive: {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{// activeProcessorCount 代表进程中激活的核心数量int count = (int)[NSProcessInfo processInfo].activeProcessorCount;count = count < 1 ? 1 : count > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : count;// 这里有5个context,按顺序存入即可context[0] = YYDispatchContextCreate("com.ibireme.yykit.user-interactive", count, qos);});return context[0];} break;case NSQualityOfServiceUserInitiated: case NSQualityOfServiceUtility://...
复制代码

从下图可以看出,不断的进行塞入任务,一个优先级只会创建四条线程(因为只有创建了4条serial queue)

以此类推,5个优先级,最多就是20条线程,控制住了线程原地爆炸的情况

总结

大体上原理,就是通过创建不同优先级的context,context中创建有限数量的queue。

在使用的时候,通过优先级获取不同的context,然后轮着返回不同的queue让你换着用,你就可以一定程度上实现并行队列,并且控制线程数量。

所以这个库来说,可以应用的范围也挺广的,在你频繁进行操作线程的地方,你都可以使用,如果你觉得自动创建的queue数量太少,那你自定义吧~

吐槽

为什么掘金不支持粘贴图片上传!!!!!!!!!!

参考

iOS 保持界面流畅的技巧

iOS中的多线程

YYDispatchQueuePool学习

YYDispatchQueuePool 学习笔记相关推荐

  1. PyTorch 学习笔记(六):PyTorch hook 和关于 PyTorch backward 过程的理解 call

    您的位置 首页 PyTorch 学习笔记系列 PyTorch 学习笔记(六):PyTorch hook 和关于 PyTorch backward 过程的理解 发布: 2017年8月4日 7,195阅读 ...

  2. 容器云原生DevOps学习笔记——第三期:从零搭建CI/CD系统标准化交付流程

    暑期实习期间,所在的技术中台-效能研发团队规划设计并结合公司开源协同实现符合DevOps理念的研发工具平台,实现研发过程自动化.标准化: 实习期间对DevOps的理解一直懵懵懂懂,最近观看了阿里专家带 ...

  3. 容器云原生DevOps学习笔记——第二期:如何快速高质量的应用容器化迁移

    暑期实习期间,所在的技术中台-效能研发团队规划设计并结合公司开源协同实现符合DevOps理念的研发工具平台,实现研发过程自动化.标准化: 实习期间对DevOps的理解一直懵懵懂懂,最近观看了阿里专家带 ...

  4. 2020年Yann Lecun深度学习笔记(下)

    2020年Yann Lecun深度学习笔记(下)

  5. 2020年Yann Lecun深度学习笔记(上)

    2020年Yann Lecun深度学习笔记(上)

  6. 知识图谱学习笔记(1)

    知识图谱学习笔记第一部分,包含RDF介绍,以及Jena RDF API使用 知识图谱的基石:RDF RDF(Resource Description Framework),即资源描述框架,其本质是一个 ...

  7. 计算机基础知识第十讲,计算机文化基础(第十讲)学习笔记

    计算机文化基础(第十讲)学习笔记 采样和量化PictureElement Pixel(像素)(链接: 采样的实质就是要用多少点(这个点我们叫像素)来描述一张图像,比如,一幅420x570的图像,就表示 ...

  8. Go 学习推荐 —(Go by example 中文版、Go 构建 Web 应用、Go 学习笔记、Golang常见错误、Go 语言四十二章经、Go 语言高级编程)

    Go by example 中文版 Go 构建 Web 应用 Go 学习笔记:无痕 Go 标准库中文文档 Golang开发新手常犯的50个错误 50 Shades of Go: Traps, Gotc ...

  9. MongoDB学习笔记(入门)

    MongoDB学习笔记(入门) 一.文档的注意事项: 1.  键值对是有序的,如:{ "name" : "stephen", "genda" ...

最新文章

  1. 支持向量机(SVM)的约束和无约束优化、理论和实现
  2. python socket server库_python基础之socket与socketserver
  3. 如何理解JavaScript原型
  4. Python OOP总结
  5. Tensorflow2.0安装(win10系统cpu版本)
  6. IDE硬盘,SATA硬盘,SCSI硬盘有什么区别
  7. keras实现DeepDream
  8. 阿里这样的高并发系统是如何做限流的?
  9. 错误:java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver
  10. ISO 27001:2022 中文试译稿
  11. powerDesigner修改mysql建表语句
  12. python高斯核函数_python 实现高斯核与卷积过程
  13. 揭开TCP传输性能优化的秘密
  14. 城市系统应用其一-表征城市交通模式
  15. 使用Docker安装MySQL
  16. 使用Python读取XMind格式测试用例,循环处理字符串
  17. 《炬丰科技-半导体工艺》RCA清洗系统及清洗液自适应预测温度
  18. 【转】微信小游戏开发源码_教程_工具_资源最新集合
  19. 眼球中心定位跟踪算法—eyelike
  20. ATX安装及简单使用

热门文章

  1. 值得关注!一种新型脑机接口--集成光子芯片的脑机接口是否可行?
  2. 荣获CHINA TOP cited paper award 2021的神经工程领域顶级期刊的论文汇总
  3. 手把手教你使用 1D 卷积和 LSTM 混合模型做 EEG 信号识别
  4. seaborn系列 (18) | 线性回归图regplot()
  5. JAVA实现判断树的子结构及树的镜像问题(《剑指offer》)
  6. “脱虚向实”大潮下:新能源自动驾驶从大厂抢人靠什么
  7. 对话图灵奖得主John Hennessy,他说对美国留学签证变化很忧心
  8. 137% YOLOv3加速、10倍搜索性能提升!这样的惊喜,最新版PaddleSlim有10个
  9. eyoucmsPHP企业网站内容管理系统
  10. SharedPreferences源码解析