不知道大家对throttle这个单词是否看着眼熟,还是说对这个计算机基础概念有很清晰的了解了。今天就来聊聊和throttle相关的一些技术场景。

定义

我经常有一种感觉,对于英语这门语言的语感,会影响我们对于一些关键技术概念的理解。有时候在学习新技术知识的时候,我会先花一些时间去了解术语英文单词的各种语义,在形成强烈清晰的语感之后,再去深入具体的技术语境。throttle也算是个生僻的单词,至少在口语中毕竟少用到,先来看看词义:

a device controlling the flow of fuel or power to an engine.

中文翻译是节流器,一种控制流量的设备。对应到我们计算机世界,可以理解成,一种控制数据或者事件流量大小的机制。这么说可能还是有些抽象,再来看看一些具体的技术场景加深理解。

场景一:GCD Background Queue

话说GCD几乎是iOS面试的必问题,也是个送分题:)。

我一般会机械式的先问:GCD有哪几种Queue?回答:串行Queue和并行Queue。

我继续问:Global Queue有哪几种优先级?回答:有几种吧,大概记得Default,Low,High吧。

我双眉一挑,进一步试探:不知道少侠有没有研究过DISPATCH_QUEUE_PRIORITY_BACKGROUND作何用?问完立即竖起耳朵,殷殷期盼萦绕于心的关键字。如果能听到「I/O Throttle 呀!」,我会瞬间觉得面试气氛被点亮了。

当然啦,答不出I/O Throttle并不能说明技术不扎实,但能答出来,至少表明对待技术是有好奇心的,加分!

官方文档如是说:

Items dispatched to the queue run at background priority; the queue is scheduled for execution after all high priority queues have been scheduled and the system runs items on a thread whose priority is set for background status. Such a thread has the lowest priority and any disk I/O is throttled to minimize the impact on the system.

那Disk I/O Throttle做什么用呢?按照上面这段描述,Disk I/O会impact system performance。

理解Disk I/O的影响需要补充一些大学课本上的知识。一次磁盘读写操作涉及到的硬件资源主要有两个,CPU和磁盘。任务本身由CPU触发和调度,读操作发生时,CPU告知Disk去获取某个地址的数据,此时由于Disk的读操作存在寻址延迟,CPU是处于I/O wait状态,一直维持到Disk返回数据为止。处于I/O wait状态的CPU,此时并不能把这部分等待的时间用来处理其他任务,也就是说这一段等待的CPU时间被“浪费”了。而CPU是公共的系统资源,这部分资源的损耗自然会对系统的整体表现产生负面影响。即使Global Queue使用的是子线程,也会造成CPU资源的消耗。

如果把任务的Priority调整为DISPATCH_QUEUE_PRIORITY_BACKGROUND,那么这些任务中的I/O操作就被被控制,虽然具体的控制策略并没有官方文档描述(一种可能的策略是并发的Disk I/O变为串行的),但我们能确认的是,部分I/O操作的启动时间很有可能被适当延迟,把更多的CPU资源腾出来处理其他任务(比如说一些系统资源的调度任务),这样可以让我们的系统更加稳定高效。简而言之,对于重度磁盘I/O依赖的后台任务,如果对实时性要求不高,放到DISPATCH_QUEUE_PRIORITY_BACKGROUND Queue中是个好习惯,对系统更友好。

实际上I/O Throttle还分为好几种,有Disk I/O Throttle,Memory I/O Throttle,和Network I/O Throttle。语义类似只不过场景不同,继续往下看。

场景二:ASIHttpRequest Network Throttle

早几年读ASIHttpRequest源码的时候,读到过一段有意思的代码:

1
2
3
4
5
6
- (void)handleNetworkEvent:(CFStreamEventType)type
{
//...
    [self performThrottling];
//...
}

在AFNetworking中也有类似的代码:

1
2
3
4
5
6
7
8
9
10
/**
 Throttles request bandwidth by limiting the packet size and adding a delay for each chunk read from the upload stream.
 When uploading over a 3G or EDGE connection, requests may fail with "request body stream exhausted". Setting a maximum packet size and delay according to the recommended values (`kAFUploadStream3GSuggestedPacketSize` and `kAFUploadStream3GSuggestedDelay`) lowers the risk of the input stream exceeding its allocated bandwidth. Unfortunately, there is no definite way to distinguish between a 3G, EDGE, or LTE connection over `NSURLConnection`. As such, it is not recommended that you throttle bandwidth based solely on network reachability. Instead, you should consider checking for the "request body stream exhausted" in a failure block, and then retrying the request with throttled bandwidth.
 @param numberOfBytes Maximum packet size, in number of bytes. The default packet size for an input stream is 16kb.
 @param delay Duration of delay each time a packet is read. By default, no delay is set.
 */
- (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes
                                  delay:(NSTimeInterval)delay;

原谅我贴了一大段注释,这段英文描述对于加深我们对于一些网络行为的理解很有帮助。

这些知名的第三方网络框架都有对Newtork Throttle的支持,你可能会好奇,我们为什么要对自己发出的网络请求做流量控制,难道不应该尽可能最大限度的利用带宽吗?

此处需要科普一点TCP协议相关的知识。我们通过HTTP请求发送数据的时候,实际上数据是以Packet的形式存在于一个Send Buffer中的,应用层平时感知不到这个Buffer的存在。TCP提供可靠的传输,在弱网环境下,一个Packet一次传输失败的概率会升高,即使一次失败,TCP并不会马上认为请求失败了,而是会继续重试一段时间,同时TCP还保证Packet的有序传输,意味着前面的Packet如果不被ack,后面的Packet就会继续等待,如果我们一次往Send Buffer中写入大量的数据,那么在弱网环境下,排在后面的Packet失败的概率会变高,也就意味着我们HTTP请求失败的几率会变大,类似这样:

大部分时候在应用层写代码的时候,估计不少同学都意识不到Newtork Throttle这种机制的存在,在弱网环境下(丢包率高,带宽低,延迟高)一些HTTP请求(比如上传图片或者日志文件)失败率会激增,有些朋友会觉得这个我们也没办法,毕竟网络辣么差。其实,作为有追求的工程师,我们可以多做一点点,而且弱网下请求的成功率其实是个很值得深入研究的方向。针对弱网场景,我们可以启用Newtork Throttle机制,减小我们一次往Send Buffer中写入的数据量,或者延迟某些请求的发送时间,这样所有的请求在弱网环境下,都能「耐心一点,多等一会」,请求成功率自然也就适当提高啦。

那么,再看AFNetworking中的这个函数,是不是更能理解了呢?

1
2
- (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes
                                  delay:(NSTimeInterval)delay;

Network Throttle体现了一句至理名言「慢即是快」。

场景三:Event Frequency Control

不知道大家在写UI的时候,有没有遇到过用户快速连续点击UIButton,产生多次Touch事件回调的场景。以前机器还没那么快的时候,我在用一些App的时候,时不时会遇到偶尔卡顿,多次点击一个Button,重复Push同一个Controller。有些工程师会在Button的点击事件里记录一个timestamp,然后判断每次点击的时间间隔,间隔过短就忽略,这也不失为一种解决办法。

再后来学习RxSwift的时候,看到:

1
2
3
4
5
6
button.rx_tap
   .throttle(0.5, MainScheduler.instance)
   .subscribeNext { _ in 
      print("Hello World")
   }
   .addDisposableTo(disposeBag)

终于有了优雅的书写方式。发现没有,throttle又出现了,这里throttle控制的是什么呢?不是disk读写,也不是network buffer,而是事件,把事件本身抽象成了一种Data,控制这种数据的流量或者产生频率,就解决了上面我们所说重复点击按钮的问题,so easy。

总结

当然还会有更多的场景,throttle其实是个基础的计算机知识。理解throttle相关的技术概念,需要在不同场景下去抽象出一个flow被节流的画面。现在,如果让你来解释一些具体的技术场景下,throttle是怎么回事,是不是可以信手拈来了:)

转载于:https://www.cnblogs.com/SensenCoder/p/6261455.html

iOS编程中throttle那些事相关推荐

  1. Go并发编程中的那些事[译]

    原文地址:Concurrent programming 原文作者:StefanNilsson 译文出自:掘金翻译计划 本文永久链接:github.com/xitu/gold-m- 译者:kobehah ...

  2. IOS 编程中引用第三方的方类库的方法及常见问题

    方法一:直接复制全部源文件到项目中 这样的方法就是把第三方类库的全部源文件复制到项目中.直接把全部.h和.m文件拖到XCode项目中就可以. 注意: 1. 假设第三方类库引用了一些系统自带类库.那么在 ...

  3. 面向对象IOS编程中的聚合与耦合

    在面向对象的设计中,我们经常会听到或用到聚合.耦合的概念.面向对象的目标就是设计出高聚合.低耦合的程序.然而,究竟什么是聚合.什么是耦合,恐怕每个人都有自己的答案,换句话说,大多数人对聚合和耦合的概念 ...

  4. iOS开发中多线程的那些事

    iOS开发深入之后还是要接触多线程和runloop的,写篇博客讲解.记录下iOS开发中的多线程技术. 线程.进程 什么是线程.进程   有的人说进程就像是人的脑袋,线程就是脑袋上的头发~~.其实这么比 ...

  5. Java编程中“为了性能”需做的26件事

    下面是参考网络资源总结的一些在Java编程中尽可能要做到的一些地方. 1.尽量在合适的场合使用单例 使用单例可以减轻加载的负担,缩短加载的时间,提高加载的效率,但并不是所有地方都适用于单例,简单来说, ...

  6. html中%3ch3%3e有颜色吗,在增强 iOS 通知系统这件事上,IFTTT 和 Pushover 谁做得更好?...

    这篇是<玩转 IFTTT,互联网自动化也可以很简单>的番外篇,主要介绍了 Pushover 这个服务,在与 IFTTT 的关联中,它有着不可替代的作用. 通知系统带来的麻烦 通知系统对于智 ...

  7. 聊聊iOS开发中耳机的那点事(监听耳机拔插、耳机线控)-b

    如果说一个项目出现的最重大的事故,那无疑就是开发人员使用了不可控的元素. 前言 iOS开发当中有关于视音频播放的开发不在少数,用户时常会使用到一种输出设备,那就是"耳机",这一篇博 ...

  8. [贝聊科技]如何在iOS开发中更好的做假数据?

    当工期比较紧的时候,项目开发中会经常出现移动端等待后端接口数据的情形,不但耽误项目进度,更让人有种无奈的绝望.所以在开发中,我们常常自己做些假数据,以方便开发和UI调试.然而做假数据方法不同,效率和安 ...

  9. iOS 开发中的多线程

    线程.进程 什么是线程.进程   有的人说进程就像是人的脑袋,线程就是脑袋上的头发~~.其实这么比方不算错,但是更简单的来说,用迅雷下载文件,迅雷这个程序就是一个进程,下载的文件就是一个线程,同时下载 ...

最新文章

  1. 在高中生物里我们提到和遗传有关的物质有染色体,染色质,基因,dna
  2. git是航空母舰:ScrumBoard
  3. 【Java 线程的深入研究3】最简单实例说明wait、notify、notifyAll的使用方法
  4. 程序员面试题精选100题(33)-在O(1)时间删除链表结点[数据结构]
  5. 类成员指针和0x0地址转换
  6. 很久没写东西了,留个言。
  7. [云炬创业基础笔记]第五章创业机会评估测试5
  8. 管家病毒查杀模块逆向分析
  9. ITK:重新缩放图像
  10. java integer 不变模式_《JAVA与模式》之不变模式
  11. python 类 字典_python基础类型—字典
  12. python如何打印文字_python怎么打印字符
  13. SVN共享链接时,避免使用服务器地址,要使用根目录(^)
  14. 190817每日一句
  15. android rom 刷机包下载地址,谷歌Android5.0 Nexus系列安卓ROM刷机包镜像/驱动下载地址发布...
  16. Android基础教程学习(一)
  17. FaceNet 读书笔记
  18. Win10的双屏显示设置
  19. PySpark | RDD持久化 | 共享变量 | Spark内核调度
  20. js中数组插入、删除元素操作

热门文章

  1. CentOS7搭建Git服务器
  2. Java的三大结构理解
  3. linux挂载与卸载(转)
  4. linux粘着位的使用(t权限)
  5. 解决Fedora没有最大化最小化按钮
  6. ubunut 下关闭apache服务自动启动
  7. 选择一线一张床还是小城一套房?
  8. Nginx:几种负载均衡调度策略配置
  9. Android --- 选项卡背景样式,左侧边有颜色
  10. 本地html自动跳转,HTML页面跳转的5种方法