当前位置: 首页 > 科技观察

iOS编程中关于油门的那些事儿

时间:2023-03-20 14:17:15 科技观察

不知道大家对油门这个词是不是很熟悉,或者对这个电脑的基本概念有没有清晰的认识。今天就来聊聊与throttle相关的一些技术场景。定义我经常有一种感觉,英语作为一种语言的感觉会影响我们对一些关键技术概念的理解。有时在学习新技术知识时,我会花一些时间去理解英文术语的各种语义,在形成强烈清晰的语感后,再深入具体的技术语境。Throttle也是一个不常见的词,至少在英语口语中很少使用。让我们先看一下含义:控制燃料或动力流向发动机的装置。中文翻译是节流阀,一种控制流量的装置。对应到我们的计算机世界,可以理解为一种控制数据或事件流量大小的机制。说到这里可能还是有点抽象,下面我们来看一些具体的技术场景来加深理解。场景一:GCD后台队列据说GCD几乎是iOS面试的必考题,也是分题:)。我通常会先机械地问:GCD有哪些Queues?答:串行队列和并行队列。我继续问:全局队列有哪些优先级?答:有几种,大概记得Default,Low,High。我挑了挑眉,进一步探究:不知少侠有没有研究过DISPATCH_QUEUE_PRIORITY_BACKGROUND的使用?问完之后,他立刻竖起了耳朵,眼巴巴地期待着那个萦绕在心头的关键词。如果我能听到“I/OThrottle!”,我会瞬间觉得面试的气氛被点燃了。当然,不会回答I/OThrottle并不代表技术不扎实,但能回答至少说明对技术有好奇心,加分!官方文档说:Itemsdispatchedtothequeuerunatbackgroundpriority;在调度了所有高优先级队列并且系统在其优先级设置为后台状态的线程上运行项目后,队列被调度执行。这样的线程具有最低的优先级,任何磁盘I/O都会受到限制,以最大限度地减少对系统的影响。磁盘I/O节流有什么用?根据上面的描述,DiskI/O会影响系统性能。了解磁盘I/O的影响需要大学教科书中的额外知识。磁盘读写操作主要涉及两个硬件资源,CPU和磁盘。任务本身由CPU触发和调度。当发生读操作时,CPU会告诉Disk获取某个地址的数据。此时由于Disk读操作的寻址延迟,CPU处于I/O等待状态,直到Disk返回。到目前为止的数据。处于I/O等待状态的CPU此时不能利用这部分等待时间来处理其他任务,也就是说等待的CPU时间被“浪费”了。CPU是一种公共的系统资源,这部分资源的流失自然会对系统的整体性能造成负面影响。GlobalQueue即使使用子线程,也会消耗CPU资源。如果将tasks的Priority调整为DISPATCH_QUEUE_PRIORITY_BACKGROUND,那么这些tasks中的I/O操作就会被控制,虽然官方文档中没有描述具体的控制策略(一种可能的策略是concurrentDiskI/O变成serial),但是我们可以确认的是,一些I/O操作的开始时间很可能会被适当延迟,腾出更多的CPU资源来处理其他的任务(比如一些系统资源调度任务),从而使我们的系统更加稳定和高效.总之,对于磁盘I/O依赖比较大的后台任务,如果实时性要求不高,把它们放在DISPATCH_QUEUE_PRIORITY_BACKGROUND队列中是个好习惯,这样对系统比较友好。实际上,I/OThrottle有几种类型,包括DiskI/OThrottle、MemoryI/OThrottle和NetworkI/OThrottle。语义相似但场景不同,继续往下看。现场二:ASIHttpRequestNetworkThrottle早几年读ASIHttpRequest源码的时候,读到过一段有意的代码:-(void)handleNetworkEvent:(CFStreamEventType)type{//...[selfperformThrottling];//...}在AFNetworking中也有类似的代码:/**通过限制数据包大小并为从上传流中读取的每个块添加延迟来限制请求带宽。通过3G或EDGE连接上传时,请求可能会失败并显示“请求正文流耗尽”。根据推荐值(`kAFUploadStream3GSuggestedPacketSize`和`kAFUploadStream3GSuggestedDelay`)设置最大数据包大小和延迟可降低输入流超过其分配带宽的风险。不幸的是,没有明确的方法来区分NSURLConnection上的3G、EDGE或LTE连接。因此,不建议您仅根据网络可达性来限制带宽。相反,您应该考虑在失败时检查“请求主体流耗尽”b锁定,然后使用受限的带宽重试请求。@paramnumberOfBytes最大数据包大小,以字节数表示。输入流的默认数据包大小为16kb。@paramdelay每次读取数据包的延迟时间。默认情况下,不设置延迟。*/-(void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytesdelay:(NSTimeInterval)delay;请原谅我发表大量评论。这个英文描述对于加深我们对一些网络行为的理解很有帮助。知名的第三方网络框架都支持NewtorkThrottle。您可能想知道为什么我们需要控制我们发出的网络请求的流量。我们不应该尽可能地最大限度地利用带宽吗?这里需要对TCP协议有一点了解。当我们通过HTTP请求发送数据时,数据实际上以Packet的形式存在于一个SendBuffer中,应用层通常感知不到这个Buffer的存在。TCP提供可靠的传输。在弱网络环境下,Packet传输失败的概率会增加。即使失败一次,TCP也不会立即认为请求失败,而是会持续重试一段时间。同时,TCP也保证了Packets的有序传输,也就是说如果前面的Packets没有ack,后面的Packets会继续等待。如果我们一次性向SendBuffer写入大量数据,那么在弱网络环境下,后续的Packets都会失败。概率会变高,也就是说我们HTTP请求失败的概率会变大,类似这样:大部分时候在应用层写代码的时候,估计很多同学都不知道Newtork的存在油门机构。在弱网络环境下(高丢包率、低带宽、高延迟),某些HTTP请求(如上传图片或日志文件)的失败率会急剧增加。有的朋友可能会觉得我们也无能为力,毕竟网络这么差。其实作为有抱负的工程师,我们可以做得更多一点,弱网下的请求成功率其实是一个值得深入研究的方向。对于弱网场景,我们可以开启NewtorkThrottle机制,减少我们一次写入SendBuffer的数据量,或者延迟部分请求的发送时间,让所有的请求在弱网中都能“忍耐”环境,再等等”,请求成功率自然会适当提高。那么,看看AFNetworking中的这个函数,是不是比较好理解?-(void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytesdelay:(NSTimeInterval)delay;NetworkThrottle体现一句名言“慢即是快”场景三:事件频率控制不知道大家有没有遇到过这样的场景,用户在写UI的时候快速连续点击UIButton,导致多次Touch事件回调,机器没那么快的时候,我在用一些app的时候,偶尔会遇到卡顿,多次点击一个Button,重复push同一个Controller,有的工程师会在Button的点击事件中记录一个时间戳,然后判断每次点击的时间间隔,如果间隔太短就忽略,这也是一种解决方法,后来在学习RxSwift的时候看到:button.rx_tap.throttle(0.5,MainScheduler.instance)。秒ubscribeNext{_inprint("HelloWorld")}.addDisposableTo(disposeBag)终于有了优雅的写法。没找到,油门又出现了,这里的油门控制什么?不是磁盘读写,也不是网络缓冲区,而是事件,把事件本身抽象成一种Data,控制这种数据的流向或者产生频率,解决我们上面说的重复点击按钮的问题,soeasy.总结当然会有更多的场景,throttle其实是一个计算机基础知识。要理解节流相关的技术概念,需要抽象出一张流量在不同场景下被节流的图。现在,如果你被要求解释在一些特定的技术场景中油门是怎么回事,你能信手拈来吗:)