上海品茶

您的当前位置:上海品茶 > 报告分类 > PDF报告下载

2019年基于协程的编程方式在移动端研发的思考及最佳实践.pdf

编号:95943 PDF 69页 5.33MB 下载积分:VIP专享
下载报告请您先登录!

2019年基于协程的编程方式在移动端研发的思考及最佳实践.pdf

1、基于协程的编程方式在移动端研发的思考及最佳实践阿里巴巴 无线开发专家目录超级App的性能和代码痛点 iOS异步编程现状 其他语言的异步编程方式 协程简介及实现原理 协程开发框架介绍 使用案例以及工程落地简介 后续的发展 总结 参考超级App的性能和代码痛点线程使用特别方便,但是多线程使用不当引发的崩溃问题很多多线程访问引发野指针问题 多线程访问引发容器类崩溃问题 多线程访问引发过渡释放问题.超级App的性能和代码痛点为了解决多线程崩溃问题或者为了让代码可读性更强可能会严重牺牲应用性能iOS系统API设计很不友好,绝大部分IO、跨进程调用等耗时接口提供的都是同步方法,主线程调用会产生严重性能问题

2、 为了解决多线程崩溃加的锁、信号量等,由于设计不合理,很容易引发卡顿甚至死锁 iOS系统API缺乏统一的异步编程模型,Delegate、Callback、同步等杂揉在一起,要写出高性能代码需要付出极大的努力超级App的性能痛点手机淘宝稳定性和性能问题主线程多线程启动线程50+常驻线程30+最大线程100+崩溃问题分布整个生命周期大量使用线程,多线程使用不当引发的崩溃问题占比达到了60%以上超级App的性能痛点手机淘宝稳定性和性能问题卡顿问题分布8%解析6%计算8%锁6%IO16%系统API56%系统API使用不当,容易造成主线程卡顿 代码中的多线程锁对应用的性能和稳定性产生了极大影响 主线程I

3、O等问题不断出现,严重影响淘宝性能系统API、IO等接口在异步编程上支持并不友好,极易产生性能问题超级App的性能和代码痛点高性能代码不好写-(void)synchronize self handleDict:self.clientVersionDict;self.clientVersionDict writeToFile:self clientVersionFilePath atomically:YES;同步接口处理字典,同步IO操作超级App的性能痛点高性能代码不好写-(void)synchronize1 dispatch_async(dispatch_get_global_queue(0

4、,0),self handleDict:self.clientVersionDict;self.clientVersionDict writeToFile:self clientVersionFilePath atomically:YES;);发现卡顿,增加异步访问逻辑超级App的性能痛点高性能代码不好写-(void)synchronize2 dispatch_async(dispatch_get_global_queue(0,0),self.versionLock lock;self handleDict:self.clientVersionDict;self.clientVersionDi

5、ct writeToFile:self clientVersionFilePath atomically:YES;self.versionLock unlock;);内部字典访问引发多线程崩溃,增加锁逻辑超级App的性能痛点高性能代码不好写简单的逻辑越来越复杂,最终变成了灾难性的后果未来应该如何发展?Emas全面监控 流程式被动防御修复问题赋能业务 提升代码质量,升级体验崩溃监控卡顿监控内存监控编码规范实施落地CodeReview单元测试编程方式升级iOS异步编程现状iOS异步编程方式发展过程delegateblock响应式协程?逻辑割裂,可读性差共享delegate,线程不安全主要用在一些U

6、I扩展中回调地狱容易引发严重错误目前使用最广泛理念先进,概念复杂学习成本高调试困难历史悠久,现代语言标配概念简单,操作方便也许是未来iOS异步编程现状Block的缺点NSURLConnection sendAsynchronousRequest:rq queue:nil completionHandler:(NSURLResponse*_Nullable response,NSData*_Nullable data,NSError*_Nullable connectionError)if(connectionError)if(callback)callback(nil,nil,connecti

7、onError);else dispatch_async(dispatch_get_global_queue(0,0),NSDictionary*dict=NSJSONSerialization JSONObjectWithData:data options:0 error:nil;NSString*imageUrl=dictimage;NSURLConnection sendAsynchronousRequest:NSURLRequest requestWithURL:NSURL URLWithString:imageUrl queue:nil completionHandler:(NSUR

8、LResponse*_Nullable response,NSData*_Nullable data,NSError*_Nullable connectionError)dispatch_async(dispatch_get_global_queue(0,0),if(connectionError)callback(nil,dict,connectionError);else UIImage*image=UIImage alloc initWithData:data;if(callback)(image,dict,nil);););iOS异步编程现状Block的缺点容易进入嵌套地狱 错误处理复

9、杂和冗长 容易忘记调用completion handler 条件执行变得很困难 从互相独立的调用中组合返回结果变得极其困难 在错误的线程中继续执行(如子线程操作UI)其他语言的异步编程方式Kotlin中的异步编程fun postItem(item:Item)preparePostAsync token-submitPostAsync(token,item)post-processPost(post)fun preparePostAsync(callback:(Token)-Unit)/发起请求并立即返回 /设置稍后调用的回调 回调方式参考自:http:/ postItem(item:Item)

10、launch val token=preparePost()val post=submitPost(token,item)processPost(post)suspend fun preparePost():Token /发起请求并挂起该协程 return suspendCoroutine /*.*/协程方式参考自:http:/ requestWithRetry(url,retryCount)if(retryCount)return new Promise(resolve,reject)=const timeout=Math.pow(2,retryCount);setTimeout()=con

11、sole.log(Waiting,timeout,ms);_requestWithRetry(url,retryCount).then(resolve).catch(reject);,timeout););else return _requestWithRetry(url,0);function _requestWithRetry(url,retryCount)return request(url,retryCount).catch(err)=if(err.statusCode&err.statusCode=500)console.log(Retrying,err.message,retryC

12、ount);return requestWithRetry(url,+retryCount);throw err;);node.js中复杂的回调逻辑参考自:https:/www.zcfy.cc/article/mastering-async-await-in-node-js-risingstack其他语言的异步编程方式node.js的异步编程参考自:https:/www.zcfy.cc/article/mastering-async-await-in-node-js-risingstackasync function requestWithRetry(url)const MAX_RETRIES

13、=10;for(let i=0;i=MAX_RETRIES;i+)try return await request(url);catch(err)const timeout=Math.pow(2,i);console.log(Waiting,timeout,ms);await wait(timeout);console.log(Retrying,err.message,i);node.js中简介的async/await逻辑协程不仅打开了异步编程的大门,还提供了大量其他的可能性协程简介及实现原理协程是什么?协程是一种在非抢占式多任务场景下生成可以在特定位置挂起和恢复执行入口的程序组件协程简介及实

14、现原理协程是什么?参考自:https:/commons.wikimedia.org/wiki/File:Coroutine.png协程简介及实现原理协程的特征非抢占式挂起执行恢复执行无需系统调用协程调度是线程安全,无需锁保存寄存器和栈不影响其他协程执行恢复之前的寄存器和栈无缝切换回之前的执行逻辑协程简介及实现原理Show Me The Code协程简介及实现原理Stackless协程Stackful协程协程简介及实现原理Stackless协程实现Kotlin中的协程代码协程简介及实现原理Stackless协程实现class extends SuspendLambda /The current

15、state of the state machine int label=0 /local variables of the coroutine A a=null Y y=null void resumeWith(Object result)if(label=0)goto L0 if(label=1)goto L1 if(label=2)goto L2 else throw IllegalStateException()L0:/result is expected to be null at this invocation a=a()label=1 result=foo(a).await(th

16、is)/this is passed as a continuation if(result=COROUTINE_SUSPENDED)return/return if await had suspended execution L1:/external code has resumed this coroutine passing the result of.await()y=(Y)result b()label=2 result=bar(a,y).await(this)/this is passed as a continuation if(result=COROUTINE_SUSPENDE

17、D)return/return if await had suspended execution L2:/external code has resumed this coroutine passing the result of.await()Z z=(Z)result c(z)label=-1/No more steps are allowed return kotlin编译器会为协程生成一个闭包 在闭包的栈空间上保存label,和resumeWith方法 await的时候会把上下文传递进去 异步方法调用结束后,会调用上下文的resumeWith方法 resumeWith方法内部会进行go

18、to跳转,由于label在栈空间上,每次resumeWith都会改变该值,最终进行正确的跳转 协程简介及实现原理Stackful协程实现co_launch(NSData*data=await(downloadDataFromUrl(url);UIImage*image=await(imageFromData(data);imageView.image=image;);协程简介及实现原理Stackful协程实现挂起co_launch 恢复downloadDataFromUrl挂起downloadDataFromUrl 恢复co_launchstack of downloadDataFromUrl

19、spfpstack frame local variablesstack of co_launchspfpstack frame local variables协程开发框架介绍coobjc协程coobjcasyncawaityieldresume协程内核context(asm)continuationchannelschedulersequencecokit系统IO API系统卡顿API计算API并发模型canceljoinnext存储APIDebug线程追踪调用链溯源context切换记录调度状态异常记录堆栈dump协程开发框架介绍协程的操作符和使用场景async/awaitco_launc

20、h创建协程 await执行异步方法,挂起等待结果返回,并回到执行线程 cancel取消协程执行异步网络请求、磁盘IO访问、数据库访问等多异步组合操作例如多图上传、多文件下载等等待事件,延迟执行,例如AlertView显示使用场景参考自:https:/dkandalov.github.io/async-await协程开发框架介绍协程的操作符和使用场景协程开发框架介绍协程的操作符和使用场景无需dispatch,代码可读性更强 消除了锁的使用 代码极其简洁协程开发框架介绍协程的操作符和使用场景generatorcreate generatoryieldyieldyield.customernextn

21、extnext.pausepausepausepausevalue1resumevalue2resumevalue2resume协程开发框架介绍协程的操作符和使用场景NSArray,NSSet,NSDictionarya Containerproducean Enumerationalways isnext()produce next valueClassical Containergeneratora generator functiona generatorisan Enumerationalways isnext()lazily produce next value传统容器类是一个把元素

22、组织在一起的数据结构,在遍历时需要所有数据都已经准备好传统容器类的遍历并不是线程安全的,会引发崩溃问题generator是一种懒生成数据的方法,在创建好了之后处于挂起状态,只有每次调用next才会产生数据generator内部实现是线程安全的,可以在一个线程生成数据,另外一个线程消费数据协程开发框架介绍使用Generator进行XML解析 Ajax and XUL https:/www.xul.fr/en/XML graphical interface etc.https:/www.xul.fr/xul-icon.gif https:/www.xul.fr/en/index.php News

23、of today https:/www.xul.fr/en-xml-rss.html All you need to know about RSS News of tomorrows https:/www.xul.fr/en-xml-rdf.html And now,all about RDF 协程开发框架介绍使用Delegate进行XML解析interface ViewController()/0:初始化状态,1:处于channel中,2:处于image中,3:处于item中,4:url,5:title,6:description,7:link property(nonatomic,assi

24、gn)int state;property(nonatomic,strong)NSMutableArray*states;property(nonatomic,strong)Channel*curChannel;property(nonatomic,strong)Image*curImage;property(nonatomic,strong)Item*curItem;property(nonatomic,strong)NSString*content;end协程开发框架介绍使用Delegate进行XML解析-(void)parser:(NSXMLParser*)parser didStart

25、Element:(NSString*)elementName namespaceURI:(nullable NSString*)namespaceURI qualifiedName:(nullable NSString*)qName attributes:(NSDictionary*)attributeDict if(elementName isEqualToString:channel).else if(elementName isEqualToString:image).else if(elementName isEqualToString:item).-(void)parser:(NSX

26、MLParser*)parser foundCharacters:(NSString*)string self.content=string;-(void)parser:(NSXMLParser*)parser didEndElement:(NSString*)elementName namespaceURI:(nullable NSString*)namespaceURI qualifiedName:(nullable NSString*)qName if(elementName isEqualToString:image).else if(elementName isEqualToStri

27、ng:item).协程开发框架介绍使用Delegate进行XML解析逻辑实现复杂无法按需解析协程开发框架介绍使用Generator进行XML解析COCoroutine*xml_generator=NSXMLParser co_parseData:data;co_launch(COXMLItem*item=nil;.while(1)item=xml_generator next;if(item.itemType=COXMLItemDidStartElement&item.elementName isEqualToString:image)image=NSMutableDictionary all

28、oc init;if(item.itemType=COXMLItemDidEndElement&item.elementName isEqualToString:image)chanel.images addObject:image;image=nil;if(item.itemType=COXMLItemDidStartElement&item.elementName isEqualToString:item)dataItem=NSMutableDictionary alloc init;.);协程开发框架介绍使用Generator进行XML解析逻辑实现简单可以按需解析协程开发框架介绍协程的操

29、作符和使用场景actormailbox:存储message的队列Isolated State:actor的状态,内部变量等message:类似于OOP的方法调用的参数状态私有 只能接收和处理消息 每个actor一次执行一个消息参考自:https:/cwiki.apache.org/confluence/display/FLINK/Akka+and+Actors协程开发框架介绍协程的操作符和使用场景actor传统OOP调用actor状态私有,不可共享actor通过消息进行通信,不需要传递执行线程actor内部串行处理消息,可以保障内部状态和变量的正确性OOP状态可共享,外部可以访问和改变OOP方

30、法调用会传递执行线程OOP方法调用不保证执行线程,因此需要使用锁来确保内部状态和变量的正确性参考自:https:/doc.akka.io/docs/akka/current/guide/actors-intro.html参考自:https:/doc.akka.io/docs/akka/current/guide/actors-motivation.html协程开发框架介绍OOP实现的计数器interface Counter:NSObject property(nonatomic,assign)int count;-(void)incCount;-(int)getCount;end implem

31、entation Counter-(void)incCount _count+;-(int)getCount return _count;end协程开发框架介绍OOP实现的计数器线程不安全协程开发框架介绍OOP实现的计数器interface Counter:NSObject property(nonatomic,assign)int count;-(void)incCount;-(int)getCount;end implementation Counter-(void)incCount synchronized(self)_count+;-(int)getCount synchronized

32、(self)return _count;end协程开发框架介绍OOP实现的计数器线程安全,但是加锁容易引发卡顿协程开发框架介绍Actor实现的计数器CCOActor*countActor=co_actor_onqueue(CCOActorChan*channel)int count=0;for(CCOActorMessage*message in channel)if(message stringType isEqualToString:inc)count+;else if(message stringType isEqualToString:get)plete(count);,get_tes

33、t_queue();co_launch(countActor sendMessage:inc;countActor sendMessage:inc;int currentCount=await(countActor sendMessage:get)intValue;);co_launch_onqueue(countActor sendMessage:inc;int currentCount=await(countActor sendMessage:get)intValue;,dispatch_queue_create(counter queue1,NULL);协程开发框架介绍Actor实现的计

34、数器线程安全,并且不用加锁使用案例以及工程落地简介使用案例以及工程落地简介Lottie性能优化-(void)_setImageForAsset:(LOTAsset*)asset if(asset.imageName)UIImage*image;if(asset.rootDirectory.length 0).id imageCache=LOTCacheProvider imageCache;if(imageCache)image=imageCache imageForKey:imagePath;if(!image)image=UIImage imageWithContentsOfFile:im

35、agePath;imageCache setImage:image forKey:imagePath;else image=UIImage imageWithContentsOfFile:imagePath;else NSString*imagePath=asset.assetBundle pathForResource:asset.imageName ofType:nil;image=UIImage imageWithContentsOfFile:imagePath;/try loading from asset catalogue instead if all else fails if(

36、!image)image=UIImage imageNamed:asset.imageName inBundle:asset.assetBundle compatibleWithTraitCollection:nil;.使用案例以及工程落地简介Lottie性能优化Lottie初始化阶段的图片加载等操作全部在主线程完成,性能低下使用案例以及工程落地简介Lottie性能优化-(void)_setImageForAsset:(LOTAsset*)asset if(asset.imageName)UIImage*image;if(asset.rootDirectory.length 0).id ima

37、geCache=LOTCacheProvider imageCache;if(imageCache)image=imageCache imageForKey:imagePath;if(!image)image=await(UIImage co_imageWithContentsOfFile:imagePath);imageCache setImage:image forKey:imagePath;else image=await(UIImage co_imageWithContentsOfFile:imagePath);else NSString*imagePath=asset.assetBu

38、ndle pathForResource:asset.imageName ofType:nil;image=await(UIImage co_imageWithContentsOfFile:imagePath);/try loading from asset catalogue instead if all else fails if(!image)image=await(UIImage co_imageNamed:asset.imageName inBundle:asset.assetBundle compatibleWithTraitCollection:nil);.使用案例以及工程落地简

39、介Lottie性能优化Lottie协程化改造后,初始化不会影响主线程性能,体验大大提升手机淘宝改造前 需要20+次主线程图片IO用于Lottie初始化 上海品茶启动在低端机型上有明显卡顿手机淘宝改造后 需要0次主线程图片IO用于Lottie初始化 上海品茶启动耗时在低端机型上降低10%左右使用案例以及工程落地简介异步转同步/*同步取缓存位置数据 *param option LocationOption *return LocationData */-(LocationData*)getCacheLocation:(LocationOption*)option /位置:异步转同步 dispatch_se

40、maphore_t locationSemaphore=dispatch_semaphore_create(0);_block LocationData*locationData=nil;void(cacheLocationBlock)(TBLocationData*aData,NSError*error)=(LocationData*aData,NSError*error)locationData=aData;dispatch_semaphore_signal(locationSemaphore);dispatch_async(LocationThread shareLocationThre

41、ad getLocationThreadQueue,self requestLocation:cacheLocationBlock;);dispatch_time_t after=dispatch_time(DISPATCH_TIME_NOW,5*NSEC_PER_SEC);dispatch_semaphore_wait(locationSemaphore,after);return locationData;使用案例以及工程落地简介异步转同步使用信号量异步转同步容易造成卡死使用案例以及工程落地简介异步转同步-(LocationData*)co_getCacheLocation:(Locati

42、onOption*)option SURE_ASYNC if(COCoroutine currentCoroutine)return await(self async_getCacheLocation:option);else return nil;使用案例以及工程落地简介异步转同步使用协程异步转同步,消除异步转同步引发的卡顿或者卡死问题手机淘宝改造前 低端机可能出现启动卡死 滑动过程中容易卡顿手机淘宝改造后 异步转同步引发的卡死问题降低为0 低端机型上上海品茶平均滑动帧率提升15%左右使用案例以及工程落地简介多值返回co_launch(NSMutableArray*list=NSMutableA

43、rray array;for(NSString*url in urls)NSDictionary*dict=await(co_downloadJSONWithURL(url);DetailModel*model=await(co_detailModelFromJSON(dict);list addObject:model;self.models=list;self.tableView reloadData;);interface DetailRequestResult:NSObject property(nonatomic,strong)NSDictionary*dict;property(n

44、onatomic,strong)NSURLResponse*response;property(nonatomic,strong)NSError*error;end使用案例以及工程落地简介多值返回如果要想接收多值返回,需要定义新的数据结构使用案例以及工程落地简介使用元组返回多值co_launch(NSMutableArray*list=NSMutableArray array;for(NSString*url in urls)NSError*error=nil;NSURLResponse*response=nil;NSDictionary*dict=nil;co_unpack(&dict,&r

45、esponse,&error)=await(co_downloadJSONWithURL(url);if(!error|response.statusCode!=200)list addObject:errorModel;else DetailModel*model=await(co_detailModelFromJSON(dict);list addObject:model;self.models=list;self.tableView reloadData;);使用案例以及工程落地简介使用元组返回多值使用元组可以让我们方便的传递多值使用案例以及工程落地简介协程使用注意事项协程并不是万能的协

46、程适用的场景 IO密集型调用 Generator式的流式计算 消除多层回调,利用协程的同步模型,降低开发成本,提升代码可读性使用案例以及工程落地简介协程使用注意事项协程不适合使用的场景 CPU计算密集型场景 后续的发展?IO?UI异步非阻塞升级:IO、计算、网络、消息、异步UI 提供全新的并发编程模型(如squence)减少多线程、锁滥用 减少主线程卡顿 减少多线程crash、降低异步问题排查难度更方便控制任务 暂停|恢复|终止非阻塞IO消费者模型 并发-串行并发编程模型async/awaitgeneratoractor减少线程使用减少锁的使用异步非阻塞提升代码质量减少多线程Crash减少主线程卡顿后续的发展推进手机淘宝异步编程全协程化升级卡顿&卡死问题减少20%多线程崩溃问题减少30%节省20%左右的异步代码协程的优势简明概念少 原理简单 易用清晰性能使用简单 上手快 改造方便 同步写异步逻辑 同步代码符合思维习惯 可读性高 减少线程切换 降低锁的使用 程序是写来给人读的,只会偶尔让机器执行一下Abelson and Sussman作者总结基于协程实现的编程范式能够帮助开发者编写出更加优美、健壮、可读性更强的代码协程可以帮助我们在编写并发代码的过程中减少线程和锁的使用,提升应用的性能和稳定性

友情提示

1、下载报告失败解决办法
2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
4、本站报告下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。

本文(2019年基于协程的编程方式在移动端研发的思考及最佳实践.pdf)为本站 (云闲) 主动上传,三个皮匠报告文库仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知三个皮匠报告文库(点击联系客服),我们立即给予删除!

温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载不扣分。
会员购买
客服

专属顾问

商务合作

机构入驻、侵权投诉、商务合作

服务号

三个皮匠报告官方公众号

回到顶部