首页 文章 精选 留言 我的

精选列表

搜索[全模态推理],共10000篇文章
优秀的个人博客,低调大师

Android UI优化全解析

点击上方蓝字关注我,每天一见,给你力量 前言 往期优化文章目录: 启动优化 今天继续优化方面的内容——UI(布局)优化。 UI优化知识点主要分为三部分: 第一部分,系统为我们做的优化。由于前端中UI展示的特殊性和重要性,Android团队也是在不断想办法提高UI方面的渲染速度,所以也是更新了很多系统优化方案,比如: 硬件加速、黄油计划、RenderThread。 第二部分,我们可以 具体实施的优化方案。主要包括: java代码布局、View重用、异步创建View、xml布局优化、异步布局框架Litho、屏幕适配、Flutter、Jetpack Compose 第三部分,工具使用,主要包括: Choreographer、monitor、Systrace 系统做的优化 硬件加速 之前我们说过,一个图形的绘制是CPU,GPU和屏幕三方合作的结果。 在Android3.0之前,还没有硬件加速,都是通过CPU进行数据计算,然后通过Skia库进行软件绘制,但是CPU对于图形处理并不高效。 于是从3.0开始,Android支持了硬件加速,到Android4.0默认开启硬件加速。 开启硬件加速后,就是由CPU进行图形缓存数据的绘制。这样CPU和GPU就能比较好的分工,各司其职了。CPU用于控制复杂绘制逻辑、构建或更新DisplayList(基础元素);GPU用于完成图形计算、渲染DisplayList(基础元素)。 这里也找了一张各种场景下,硬件加速前后的流程与加速效果(Android6.0背景): 但是硬件加速也是有缺点的: 启用硬件加速需要更多资源,因此应用会占用更多内存。 比较低的版本,由于有些 Canvas API还没有支持,所以使用硬件加速可能会有问题。那么我们就可以手动关闭某个view的硬件加速: myView.setLayerType(View.LAYER_TYPE_SOFTWARE,null) Project Butter 黄油计划,你有可能没怎么听说,但是其实之前两章内容都提到过,Android4.1之后,Google提出了黄油计划,主要包括两个内容: VSYNC Triple Buffering(三重缓存) 这些都熟悉了吧,上两节都说过的,这里再简单提一下: VSYNC 垂直同步信号,每当收到这个信号后,CPU就开始准备Buffer数据,并在16ms之内和GPU把屏幕需要的缓存数据准备好。 Triple Buffering(三重缓存) 在Android4.1之前,是双缓存机制,大部分是没问题的。但是当CPU/GPU绘制过程较长,超过一个vsync信号周期,一般是16ms,就会导致丢帧,CPU无法使用被GPU或者屏幕占用的缓存区。如果下一帧绘制如果又超时,那么又会丢帧。 所以再加上一个缓存区,这样,CPU、GPU、Display三者都有各自的缓存区,互不影响,就能保证时间的最大化利用,也就能减少上述的情况了。 RenderThread RenderThread是在Android5.0提出的,从这个名字就能知道,它是一个线程,一个专门执行GL命令的线程,也就是一部分的绘制工作。 有了它之后,当CPU处理数据给GPU后,就不需要等GPU渲染完毕了,而是将一些绘制任务交给RenderThread,这样就能减少主线程的工作,保证画面的流畅。同时还提供了RenderNode,用来做view的属性封装,渲染帧的信息等等。 优化方案 java代码布局 我们一般都是用XML文件进行布局,但是XML解析也是很耗时的,并在这个解析过程在主线程进行。 所以我们有的时候也许可以通过Java代码或者kotlin进行View的创建? 理论中,这样确实能减少布局加载的消耗时间,但是Java代码创建View太麻烦了,而且无法可视化。 当然,也有一些库可以帮助我们将xml代码转换成java代码,比如X2C(https://github.com/iReaderAndroid/X2C ),但是它并不支持所有的情况,比如merge标签,appCompat兼容控件等等。 所以我们需要在这之中找到平衡点,有的时候,UI简单并且要求高性能的前提下,我们可以试试用这样的方法,即用java代码代替XmL代码。 View重用 参照Recyclerview的做法,我们也可以将一些常用的view保存到缓存池中,这样在不同的界面中就能复用缓存池里面的view。 异步创建view 这是Google提出的一个方案——AsyncLayoutInflater。它可以异步加载布局文件,并且回调给主线程,从而减少主线程耗时。简单贴下主要代码: newAsyncLayoutInflater(MainActivity.this).inflate(R.layout.activity_main,null,newAsyncLayoutInflater.OnInflateFinishedListener(){@OverridepublicvoidonInflateFinished(@NonNullViewview,inti,@NullableViewGroupviewGroup){//回调给主线程setContentView(view);}}); xml布局优化 在写xml布局文件的时候,我们要做的也有很多,比如: 减少布局嵌套。多使用ViewStub、Merge、ConstraintLayout来代替。 优化开销。RelativeLayout和 使用weight的LinearLayout 开销比较大,建议使用ConstraintLayout,LinearLayout代替。 异步布局框架Litho Litho是Facebook开源的一款在Android上高效建立UI的声明式框架。 主要有以下特点: 1)声明式:它使用了声明式的API来定义UI组件。 2)异步布局:它把 measure 和 layout 都放到了后台线程,只留下了必须要在主线程完成的 draw,这大大降低了 UI 线程的负载 3)视图扁平化:由于 Litho 使用了自有的布局引擎(Yoga),在布局阶段就可以检测不必要的层级、减少 ViewGroups,来实现 UI 扁平化。 4)优化 RecyclerView:Litho 还优化了 RecyclerView 中 UI 组件的缓存和回收方法。 屏幕适配 关于屏幕适配问题,也是老生常谈了。主要有以下几种方案: dp适配方案。 这是系统自带的适配单位,dp是基于屏幕物理分辨率一个抽象的单位,用于说明与密度无关的尺寸和位置。所以它能在不同分辨率的手机上有相对大小的适配性。计算公式是:px=dp * (dpi/160)。但是dpi有可能会被人为调整(比如几部相同分辨率不同尺寸的手机的ppi可能分别是是430,440,450,那么在Android系统中,可能dpi会全部指定为480),所以还是有可能在一些设备上出现适配问题。 宽高限定符适配方案。 简单地说,这个方案就是穷举市面上所有的Android手机的宽高像素值。然后找到对应的文件夹使用下面的资源文件所对应的px值。 但是这方案有个缺陷,就是必须精确命中才行。比如1920x1080的手机就一定要找到1920x1080的限定符,否则就只能用统一的默认的dimens文件了。 所以容错性太低,不推荐。 smallestWidth适配方案。 这个方案就是通过手机的宽度值来找到对应限定符文件夹下的资源文件,可以看做宽高限定符屏幕适配方案的升级版。 假如我们的设计图宽为360dp,那么就创建values-sw360dp文件夹,并添加资源文件: <?xmlversion="1.0"encoding="UTF-8"?><resources><dimenname="dp_1">1dp</dimen><dimenname="dp_2">2dp</dimen><dimenname="dp_3">3dp</dimen>...<dimenname="dp_359">359dp</dimen><dimenname="dp_360">360dp</dimen></resources> 很简单,分为360份,然后我们实际写布局文件的时候,直接引用对应的dimen值即可。 然后新建其他设备宽度的文件夹,并在每个文件夹里添加对应的资源文件,这里以400dp为例: ├──src/main│├──res│├──├──values│├──├──values-sw320dp│├──├──values-sw400dp│├──├──...│├──├──values-sw640dp <?xmlversion="1.0"encoding="UTF-8"?><resources><dimenname="dp_1">1.1111dp</dimen><dimenname="dp_2">2.2222dp</dimen><dimenname="dp_3">3.3333dp</dimen><dimenname="dp_4">4.4444dp</dimen>...<dimenname="dp_359">398.8889dp</dimen><dimenname="dp_360">400.0000dp</dimen></resources> 也就是说,所有的设备都分为360份了,这样就能保证在不同宽度设备上都能有差不多的效果。 如果我们的设备宽度为400dp,那么就会调用values-sw400dp对应的资源文件,如果找不到,就会向下查找。比如我们宽度是402dp,找不到对应的,就会向上找到400dp对应的资源文件,所以也有比较好的容错性。也是一个比较好的适配方案。 当然这种重复性工作肯定不需要我们自己手动去实现,有专门的插件可以生成相应的文件和文件夹,这里也推荐一个:https://github.com/ladingwu/dimens_sw 今日头条适配方案。 这个大家应该都很熟悉了,主要是通过动态修改density值来保证所有设备的屏幕宽度都是固定的dp值。用到的公式就是px = density * dp。 比如设计图宽为360dp,我们只要保证所有设备的宽度都是360dp就能适配了。而宽度的px值我们是已知的,所以就是要修改这个 density 值来完成我们的适配目的。具体代码我就不贴了,网上很多。 这种方案侵入性低,使用方便,是个不错的适配方案。 Flutter Flutter是 Google 推出并开源的移动应用开发框架,开发者可以通过 Dart 语言开发 App,一套代码同时运行在 iOS 和 Android 平台。 Flutter框架现在也是特别火,实际运用到很多的大厂项目,比如闲鱼今日头条。它相对于Android其实是另外一套APP架构了,它没有基于系统本身的渲染引擎,而是app中自带Skia引擎,虚拟机也是使用的Dart虚拟机。 主要有以下几个特点: 跨平台:现在flutter至少可以跨5种平台,常见的平台:MacOS,Windows ,Linux ,Android ,iOS ,到目前为止,Flutter算是支持平台最多的框架了。良好的跨平台性,大大减少了开发成本。 丝滑般的体验:使用Flutter内置的Material Design(android风格)和Cupertino(ios风格)风格组件,以及丰富的motion API,平滑而自然的滑动效果和平台感知,为用户带来全新的体验。 响应式框架:使用一系列基础组件和响应式框架,可以轻松构建用户界面。使用功能强大且灵活的API可以实现复杂的界面效果。 支持插件:使用插件可以访问平台本地API,如相机,蓝牙,WIFI等等。借助现有的Java,swift ,object c , c++代码实现对原生系统的调用。 60fps超高性能:Flutter编写的应用可以达到60fps(每秒传输帧数)。Flutter采用GPU渲染技术,所以性能很好。完全可以胜任游戏开发。 Jetpack Compose Jetpack Compose 是用于构建原生 Android 界面的新工具包 原来我们的布局文件都是写在xml文件中的,现在提供了一种新的view构建方式,也就是Compose。 它是一种声明式UI,不再使用xml,而是使用kotlin进行UI布局。其实就跟我们之前提到的一点,用java代码去构建view一样的效果。这样就减少了xml解析的时间,提高了效率。 声明式UI。指的是只需要把界面声明出来,不需要手动更新。比如我们这里的Compose只需要写一遍,后续的UI改变会随着变量自动更新。而传统的xml布局方式就无法做到这一点,属于命令式UI,需要我们手动命令纸牌屋UI的修改。 官方也是宣称有以下几点优势: 更少更直观的代码,更强大的功能,能提高开发速度。 最后贴一段代码,感受下Compose的写法: classMainActivity:AppCompatActivity(){overridefunonCreate(savedInstanceState:Bundle?){super.onCreate(savedInstanceState)setContent{Greeting("Android")}}}@ComposablefunGreeting(name:String){Text(text="Hello$name!")} 工具篇 Choreographer 嘿嘿,没想到吧,上期我们说过的Choreographer其实也是一个监控应用帧率的工具。它主要有以下特性: 能获取整体的帧率。 能在线上使用。 获取的帧率几乎是实时的。 主要原理就是利用postFrameCallback计算两次绘制的间隔时间,简单贴下代码: privatelongmLastFrameTime;Choreographer.getInstance().postFrameCallback(newChoreographer.FrameCallback(){@OverridepublicvoiddoFrame(longframeTimeNanos){if(mLastFrameTime==0){mLastFrameTime=frameTimeNanos;}floatdiff=(frameTimeNanos-mLastFrameTime)/1000000.0f;//得到毫秒,正常是16.66msif(diff>500){doublefps=(((double)(mFrameCount*1000L))/diff);mFrameCount=0;mLastFrameTime=0;Log.d("doFrame","doFrame:"+fps);}else{++mFrameCount;}Choreographer.getInstance().postFrameCallback(this);}}); 想细细研究的可以看看这个库(https://github.com/friendlyrobotnyc/TinyDancer ) LayoutInspector/Android Device Monitor LayoutInspector是AndroidStudio种的一个布局检查器,可以通过Tools > Layout Inspector找到,他可以检查应用中的某个界面的视图结构,但是无法查看非调式状态的应用。 如果要看其他应用的布局情况,可以使用Android Device Monitor,在Android Studio 3.1 以后,需要单独从文件夹打开了: android-sdk/tools/monitor Systrace Systrace是分析Android性能问题的神器,获取Systrace文件的方式有两种: 一是 AndroidSDK/tools目录下,通过 monitor.bat用 Android Device Monitor可视化工具得到。 二是通过 python脚本获取。 具体怎么分析这里就不细说了,下次可以专门一篇文章讲Systrace性能分析。 参考 https://www.jianshu.com/p/a4b8e4c5d9b0 https://time.geekbang.org/column/article/81049 https://juejin.cn/post/6844904047355363341 https://juejin.cn/post/6844904048068395015 https://blog.csdn.net/u013425527/article/details/97046401 感谢大家的阅读,有一起学习的小伙伴可以关注下公众号—码上积木❤️ 每日三问知识点/面试题,积少成多。 点在看你最好看 本文分享自微信公众号 - 码上积木(Lzjimu)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

Python 系统编程 (全)

进程 1.进程 简单的说就是实现唱歌跳舞同时进行,那么就需要一个新的方法,叫做:多任务 2.多任务的概念 ①简单地说,就是操作系统可以同时运行多个任务 ②并行执行多任务只能在多核CPU上实现,但是,由于任务数量远远多于CPU的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行 ③就是说当cpu核心数量大于任务数量就是并行,反过来,就是并发 ④依照的规则有时间片轮转,优先级调度 3.进程的创建-fork ①程序:编写完毕的代码,在没有运行的时候,称之为程序 ②进程:正在运行着的代码,还有需要运行的环境等 ③fork( ): Python的os模块封装了常见的系统调用,其中就包括fork importos #注意,fork函数,只在Unix/Linux/Mac上运行,windows不可以 pid=os.fork() ifpid==0: print('哈哈1') else: print('哈哈2') #程序执行到os.fork()时,操作系统会创建一个新的进程(子进程),然后复制父进程的所有信息到子进程中 #然后父进程和子进程都会从fork()函数中得到一个返回值,在子进程中这个值一定是0,而父进程中是子进程的id号 #普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回 #getpid()是获取当前进程(主进程或子进程)的id、getppid()获取父进程的id 4.多进程修改全局变量 多进程中,每个进程中所有数据(包括全局变量)都各有拥有一份,互不影响,所以在修改全局变量的时候,两个变量相互独立 5.多次fork问题 下次遇到多进程就画图,再如: os.fork() os.fork() os.fork()#就变成了8个进程 在while True中,如果有os.fork(),程序一定崩,这就相当于fork炸弹,死循环创建进程 6.multiprocessing模块 ①multiprocessing模块提供了一个Process类来代表一个进程对象 ②跨平台的操作,fork只在linux下才有效,所以平时应该使用process,它是一个类 程序如下: frommultiprocessingimportProcess importtime deftest(): whileTrue: print("---test---") time.sleep(1) P=Process(target=test) P.start()#让这个进程开始执行test函数里的代码 whileTrue: print("---main---") time.sleep(1) 说明: #创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动 #这样创建进程比fork()还要简单 #join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步,这就与fork不同,需要等子进程结束,主进程才可以结束 7.Process语法结构 Process([group[,target[,name[,args[,kwargs]]]]]) ①target:表示这个进程实例所调用对象; ②args:表示调用对象的位置参数元组; ③kwargs:表示调用对象的关键字参数字典; ④name:为当前进程实例的别名; ⑤group:大多数情况下用不到; 8.Process类常用方法 ①is_alive():判断进程实例是否还在执行; ②join([timeout]):是否等待进程实例执行结束,或等待多少秒; ③start():启动进程实例(创建子进程); ④run():如果没有给定target参数,对这个对象调用start()方法时,就将执行对象中 的run()方法; ⑤terminate():不管任务是否完成,立即终止; 9.Process类常用属性 ①name:当前进程实例别名,默认为Process-N,N为从1开始递增的整数; ②pid:当前进程实例的PID值 ③进程的创建-Process子类: 创建新的进程还能够使用类的方式,可以自定义一个类,继承Process类,每次实例化这个类的时候,就等同于实例化一个进程对象 如果想知道程序的运行时间,可以用开始和结束的time.time()两个时间差,就代表运行时间 10.进程池pool ①当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态成生多个进程 ②但如果是上百甚至上千个目标,手动的去创建进程的工作量巨大,此时就可以用到multiprocessing模块提供的Pool方法 P0=Pool(3)#定义一个进程池,最大进程数3 foriinrange(0,10): #Pool.apply_async(要调用的目标,(传递给目标的参数元组,))(非堵塞式) #Pool.apply(worker,(i,))(堵塞式),主进程卡在这里,需要等子进程完成才能添加 #每次循环将会用空闲出来的子进程去调用目标 P0.apply_async(worker,(i,))#work是一个函数 P0.close()#关闭进程池,关闭后po不再接收新的请求 P0.join()#等待po中所有子进程执行完成,必须放在close语句之后 11.多种创建进程的方式比较 os.fork()中,子进程和父进程可以都执行,而且父进程可以不必等待子进程结束 p=process(target=xxx) p.start() #子进程和父进程都可执行 pool=Pool(3) pool.apply_async(xxx) #主进程一般用来等待,真正的任务都在子进程中执行 12.进程间通信-Queue ①Process之间有时需要通信,操作系统提供了很多机制来实现进程间的通信。 ②Queue的使用: 可以使用multiprocessing模块的Queue实现多进程之间的数据传递,Queue本身是一个消息列队程序 队列:先进先出 栈:先进后出 初始化Queue()对象时(例如:q=Queue()),若括号中没有指定最大可接收的消息数量,或数量为负值,那么就代表可接受的消息数量没有上限(直到内存的尽头) frommultiprocessingimportQueue q=Queue(3)#初始化一个Queue对象,最多可接收三条put消息 try: q.put_nowait("消息4") except: print("消息列队已满,现有消息数量:%s"%q.qsize()) Queue.qsize():返回当前队列包含的消息数量; Queue.empty():如果队列为空,返回True,反之False; Queue.full():如果队列满了,返回True,反之False; Queue.get([block[, timeout]]):获取队列中的一条消息,然后将其从列队中移除,block默认值为True Queue.get_nowait():相当Queue.get(False);应该把它放在try里面 Queue.put(item,[block[, timeout]]):将item消息写入队列,block默认值为True ③进程池中的Queue: 如果要使用Pool创建进程,就需要使用multiprocessing.Manager()中的Queue(),而不是multiprocessing.Queue() 13.孤儿进程和僵尸进程 ①孤儿进程:是指父进程结束,但子进程还未结束,通常的情况下父进程可以清除子进程 的垃圾,表示子进程没人收尸了 ②僵尸进程:是指子进程结束了,父进程还未结束 ③一般在操作系统中,0号进程负责切换任务,1号进程负责生子进程,并负责打理孤儿进程 线程 1.多线程threading python的thread模块是比较底层的模块,python的threading模块是对thread做了一些包装的,可以更加方便的被使用 2.进程和线程的关系 ①线程是进程里面一种真正执行代码的东西,类似进程里面的箭头 ②进程是资源分配的单位,线程是cpu调度的单位 ③进程,能够完成多任务,比如 在一台电脑上能够同时运行多个QQ ④线程,能够完成多任务,比如 一个QQ中的多个聊天窗口 ⑤定义的不同: 进程是系统进行资源分配和调度的一个独立单位. 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位 线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈), 但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源 ⑥区别: 一个程序至少有一个进程,一个进程至少有一个线程 线程的划分尺度小于进程(资源比进程少),使得多线程程序的并发性高 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率 线程不能够独立执行,必须依存在进程中 ⑦优缺点: 线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反 3.多线程执行 importthreading importtime defsaySorry(): print("Python才是最好的语言") time.sleep(1) if__name__=="__main__": foriinrange(5): t=threading.Thread(target=saySorry) t.start()#启动线程,即让线程开始执行 说明: #可以明显看出使用了多线程并发的操作,花费时间要短很多 #创建好的线程,需要调用start()方法来启动 #主线程会等待所有的子线程结束后才结束 4.线程执行代码的封装 ①通过使用threading模块能完成多任务的程序开发,为了让每个线程的封装性更完美,所以使用threading模块时,往往会定义一个新的子类class,只要继承threading.Thread就可以了,然后重写 run方法 ②python的threading.Thread类有一个run方法,用于定义线程的功能函数,可以在自己的线程类中覆盖该方法。而创建自己的线程实例后,通过Thread类的start方法,可以启动该线程,交给python 虚拟机进行调度,当该线程获得执行的机会时,就会调用run方法执行线程 ③多线程程序的执行顺序与多进程类似,都是不确定的 5.总结 ①每个线程一定会有一个名字,尽管上面的例子中没有指定线程对象的name,但是python会自动为线程指定一个名字。 ②当线程的run()方法结束时该线程完成。 ③无法控制线程调度程序,但可以通过别的方式来影响线程调度的方式 6.多线程-共享全局变量 ①在一个进程内的所有线程共享全局变量,能够在不适用其他方式的前提下完成多线程之间的数据共享(这点要比多进程要好) ②缺点就是,线程是对全局变量随意遂改可能造成多线程之间对全局变量的混乱(即线程非安全),在线程中,不能同时对全局变量进行修改,解决办法是轮流让线程进行修改 7.同步 ①多线程开发可能遇到的问题 ②假设两个线程t1和t2都要对num=0进行增1运算,t1和t2都各对num修改10次, num的最终的结果应该为20。但是由于是多线程访问,答案可能不一样,所以在修改时, 就要让其修改完再轮到下一个线程来修改 ③什么是同步: 同步就是协同步调,按预定的先后次序进行运行。如:你说完,我再说,"同"字从字面上 容易理解为一起动作其实不是,"同"字应是指协同、协助、互相配合 ④解决问题的思路: 系统调用t1,然后获取到num的值为0,此时上一把锁,即不允许其他现在操作num 对num的值进行+1解锁,此时num的值为1,其他的线程就可以使用num了,而且是 num的值不是0而是1 同理其他线程在对num进行修改时,都要先上锁,处理完后再解锁,在上锁的整个过程 中不允许其他线程访问,就保证了数据的正确性 8.互斥锁 ①当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制 ②某个线程要更改共享数据时,先将其锁定,此时资源的状态为锁定,其他线程不能更改;直到该线程释放资源,将资源的状态变成非锁定,其他的线程才能再次锁定该资源。互斥锁保证了每次 只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性 ③threading模块中定义了Lock类,可以方便的处理锁定: #创建锁 mutex=threading.Lock() #锁定 mutex.acquire([blocking]) #释放 mutex.release() ④如果设定blocking为True,则当前线程会堵塞,直到获取到这个锁为止(如果没有指定,那么默认为True)如果设定blocking为False,则当前线程不会堵塞 ⑤上锁解锁过程: 每次只有一个线程可以获得锁。如果此时另一个线程试图获得这个锁,该线程就会变为 &ldquo;blocked&rdquo; 状态,称为&ldquo;阻塞&rdquo;,直到拥有锁的线程调用锁的release()方法释放锁之后,锁进 入 &ldquo;unlocked&rdquo;状态。 线程调度程序从处于同步阻塞状态的线程中选择一个来获得锁,并使得该线程进入运行( running)状态。 ⑥总结: 锁的好处: 确保了某段关键代码只能由一个线程从头到尾完整地执行 锁的坏处: 阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了 由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时, 可能会造成死锁 9.多线程-非共享数据 ①对于全局变量,在多线程中要格外小心,否则容易造成数据错乱的情况发生 ②在多线程开发中,全局变量是多个线程都共享的数据,而局部变量等是各自线程的,是非共享的 10.死锁 ①举个例子:就好比是现实社会中,男女双方都在等待对方先道歉 ②在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁 ③避免死锁: #程序设计时要尽量避免(银行家算法) #添加超时时间等 ifmutex.acquire(2): 11.同步应用 可以使用互斥锁完成多个任务,有序的进程工作,这就是线程的同步 12.生产者与消费者模式 ①Python的Queue模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列 Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue ②这些队列都实现了锁原语(可以理解为原子操作,即要么不做,要么就做完),能够在多线程中直接使用 ③可以使用队列来实现线程间的同步 ④Queue的说明: 对于Queue,在多线程通信之间扮演重要的角色 添加数据到队列中,使用put()方法 从队列中取数据,使用get()方法 判断队列中是否还有数据,使用qsize()方法 队列就是用来给生产者和消费者解耦的 13.ThreadLocal 在多线程环境下,每个线程都有自己的数据。一个线程使用自己的局部变量比使用全局变量好,因为局部变量只有线程自己能看见,不会影响其他线程,而全局变量的修改必须加锁一个thread.local()变量虽然是全局变量,但每个线程都只能读写自己线程的独立副本,互不干扰。thread.local()解决了参数在一个线程中各个函数之间互相传递的问题 14.异步 ①同步调用就是你喊你朋友吃饭,你朋友在忙,你就一直在那等,等你朋友忙完了,你们一起去 ②异步调用就是你喊你朋友吃饭,你朋友说知道了,待会忙完去找你 ,你就去做别的了 pool.apply_async(func=test,callback=test2)#callback是回调 进程整理代码 1. getpid()、getppid() importos rpid=os.fork() ifrpid<0: print("fork调用失败") elifrpid==0: print("我是子进程(%s),我的父进程是(%s)"%(os.getpid(),os.getppid())) x+=1 else: print("我是父进程(%s),我的子进程是(%s)"%(os.getpid(),rpid)) print("父子进程都可以执行这里的代码") 2.多进程修改全局变量 importos importtime num=0 #注意,fork函数,只在Unix/Linux/Mac上运行,windows不可以 pid=os.fork() ifpid==0: num+=1 print('哈哈1---num=%d'%num) else: time.sleep(1) num+=1 print('哈哈2---num=%d'%num) 3.进程的创建-Process子类 frommultiprocessingimportProcess importtime importos #继承Process类 classProcess_Class(Process): #因为Process类本身也有__init__方法,这个子类相当于重写了这个方法, #但这样就会带来一个问题,我们并没有完全的初始化一个Process类,所以就不能使用从这个类继承的一些方法和属性, #最好的方法就是将继承类本身传递给Process.__init__方法,完成这些初始化操作 def__init__(self,interval): Process.__init__(self) self.interval=interval #重写了Process类的run()方法 defrun(self): print("子进程(%s)开始执行,父进程为(%s)"%(os.getpid(),os.getppid())) t_start=time.time() time.sleep(self.interval) t_stop=time.time() print("(%s)执行结束,耗时%0.2f秒"%(os.getpid(),t_stop-t_start)) if__name__=="__main__": t_start=time.time() print("当前程序进程(%s)"%os.getpid()) p1=Process_Class(2) #对一个不包含target属性的Process类执行start()方法,就会运行这个类中的run()方法,所以这里会执行p1.run() p1.start() p1.join() t_stop=time.time() print("(%s)执行结束,耗时%0.2f"%(os.getpid(),t_stop-t_start)) 4.进程池pool frommultiprocessingimportPool importos,time,random defworker(msg): t_start=time.time() print("%s开始执行,进程号为%d"%(msg,os.getpid())) #random.random()随机生成0~1之间的浮点数 time.sleep(random.random()*2) t_stop=time.time() print(msg,"执行完毕,耗时%0.2f"%(t_stop-t_start)) po=Pool(3)#定义一个进程池,最大进程数3 foriinrange(0,10): #Pool.apply_async(要调用的目标,(传递给目标的参数元祖,)) #每次循环将会用空闲出来的子进程去调用目标 po.apply_async(worker,(i,)) print("----start----") po.close()#关闭进程池,关闭后po不再接收新的请求 po.join()#等待po中所有子进程执行完成,必须放在close语句之后 print("-----end-----") 5.进程Queue的读写 frommultiprocessingimportProcess,Queue importos,time,random #写数据进程执行的代码: defwrite(q): forvaluein['A','B','C']: print'Put%stoqueue...'%value q.put(value) time.sleep(random.random()) #读数据进程执行的代码: defread(q): whileTrue: ifnotq.empty(): value=q.get(True) print'Get%sfromqueue.'%value time.sleep(random.random()) else: break if__name__=='__main__': #父进程创建Queue,并传给各个子进程: q=Queue() pw=Process(target=write,args=(q,)) pr=Process(target=read,args=(q,)) #启动子进程pw,写入: pw.start() #等待pw结束: pw.join() #启动子进程pr,读取: pr.start() pr.join() #pr进程里是死循环,无法等待其结束,只能强行终止: print''" print'所有数据都写入并且读完' 线程整理代码 1.多线程 importthreading fromtimeimportsleep,ctime defsing(): foriinrange(3): print("正在唱歌...%d"%i) sleep(1) defdance(): foriinrange(3): print("正在跳舞...%d"%i) sleep(1) if__name__=='__main__': print('---开始---:%s'%ctime()) t1=threading.Thread(target=sing) t2=threading.Thread(target=dance) t1.start() t2.start() #sleep(5)#屏蔽此行代码,试试看,程序是否会立马结束? print('---结束---:%s'%ctime()) 2.多线程-共享全局变量 fromthreadingimportThread importtime g_num=100 defwork1(): globalg_num foriinrange(3): g_num+=1 print("----inwork1,g_numis%d---"%g_num) defwork2(): globalg_num print("----inwork2,g_numis%d---"%g_num) print("---线程创建之前g_numis%d---"%g_num) t1=Thread(target=work1) t1.start() #延时一会,保证t1线程中的事情做完 time.sleep(1) t2=Thread(target=work2) t2.start() 3.死锁 importthreading importtime classMyThread1(threading.Thread): defrun(self): ifmutexA.acquire(): print(self.name+'----do1---up----') time.sleep(1) ifmutexB.acquire(): print(self.name+'----do1---down----') mutexB.release() mutexA.release() classMyThread2(threading.Thread): defrun(self): ifmutexB.acquire(): print(self.name+'----do2---up----') time.sleep(1) ifmutexA.acquire(): print(self.name+'----do2---down----') mutexA.release() mutexB.release() mutexA=threading.Lock() mutexB=threading.Lock() if__name__=='__main__': t1=MyThread1() t2=MyThread2() t1.start() t2.start() 4.生产者和消费者模式 importthreading importtime #python2中 fromQueueimportQueue #python3中 #fromqueueimportQueue classProducer(threading.Thread): defrun(self): globalqueue count=0 whileTrue: ifqueue.qsize()<1000: foriinrange(100): count=count+1 msg='生成产品'+str(count) queue.put(msg) print(msg) time.sleep(0.5) classConsumer(threading.Thread): defrun(self): globalqueue whileTrue: ifqueue.qsize()>100: foriinrange(3): msg=self.name+'消费了'+queue.get() print(msg) time.sleep(1) if__name__=='__main__': queue=Queue() foriinrange(500): queue.put('初始产品'+str(i))

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册