Python-GIL 进程池 线程池

    五、GIL vs 互斥锁(*****)        一、什么是GIL(Global  Interpreter  Lock)            GIL是全局解释器锁,是加到解释器身上的,保护的就是解释器级别的数据 (好比垃圾回收的数据)            同一个进程内的全部线程都须要先抢到GIL锁,才能执行解释器代码        2 为何须要GIL          python 中内存管理依赖于 GC(一段用于回收内存的代码) 也须要一个线程            除了你本身开的线程 系统还有一些内置线程   就算你的代码不会去竞争解释器  内置线程也可能会竞争            因此必须加上锁        三、GIL的影响            GIl会限制同一进程的内的多个线程同一时间只能有一个运行,也就是说python一个进程内的多线线程    没法实现并行的效果,即没法利用多核优点            而后多核提供的优点是同一时刻有多个cpu参与计算,意味着计算性能地提高,也就是说咱们的任务是            计算密集型的状况下才须要考虑利用多核优点,此时应该开启python的多进程            在咱们的任务是IO密集型的状况下,再多的cpu对性能的提高也用处不大,也就说多核优点在IO密集型程序面前            发挥的做用微乎其微,此时用python的多线程也是能够的        GIL的优缺点:            优势:                保证Cpython解释器内存管理的线程安全            缺点:                同一进程内全部的线程同一时刻只能有一个执行,没法利用多核CPU                也就说Cpython解释器的多线程没法实现并行        (问题: 一个py程序 要想运行 必须运行解释器 解释器的工做时翻译代码 并执行           当一个py进程中 有多个线程 线程的任务就是执行代码  意味者 多个线程都要使用解释器           简单的说 多线程会争抢解释器的执行权           若是是本身开的线程  多线程要访问相同数据 加锁就能解决           可是有一写代码不是程序员写的 也确实须要共享使用 就是解释器        GC:垃圾回收器  负责清理内存中的无用数据 清理垃圾也须要执行代码  可是GC不该该卡住用户的代码执行            只能开线程            GC 看到 x = 10   x = 1   准备删除10  这时候忽然CPU切到用户线程  a = 10  此此时尚未问题            紧接着 CPU 又切到GC  GC上来就删除10  在切到用户线程 a 所指向的地址被清理了 产生错误        解决方案: 给解释器加上锁  保证GC执行期间 用户线程不能执行)        四、GIL vs 自定义锁            保护不一样的数据就应该加不一样的锁。            相同点:都是互斥锁            不一样点:                 GIL解释器级别锁 锁的是解释器代码                 自定义锁 锁的是本身写的代码            GIL 在当一个线程调用解释器时 自动加锁  在IO阻塞时或线程代码执行完毕/执行时间过长3ms时 自动解锁            本质就是一个互斥锁,而后保护不一样的数据就应该用不一样的互斥锁,保护咱们应用程序级别的数据必须自定义互斥锁            有了GIL 为何还须要自定义锁?                GIL 不清楚什么代码会形成数据竞争问题  不知道什么地方该加    6 Cpython的解释器下,多线程是鸡肋?*****        多个任务是IO密集型:多线程  (IO的速度 明显要比CPU执行速度慢)        多个任务是计算密集型:多进程    七、死锁现象与递归锁(可重入锁),信号量(**)      死锁?        进程也有死锁与递归锁,在进程那里忘记说了,放到这里一切说了额        所谓死锁: 是指两个或两个以上的进程或线程在执行过程当中,因争夺资源而形成的一种互相等待的现象,                若无外力做用,它们都将没法推动下去。此时称系统处于死锁状态或系统产生了死锁,                这些永远在互相等待的进程称为死锁进程,以下就是死锁        解决方法,递归锁,在Python中为了支持在同一线程中屡次请求同一资源,python提供了可重入锁RLock。            这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,            从而使得资源能够被屡次require。直到一个线程全部的acquire都被release,            其余的线程才能得到资源。上面的例子若是使用RLock代替Lock,则不会发生死锁:        mutexA=mutexB=threading.RLock() #一个线程拿到锁,counter加1,该线程内又碰到加锁的状况,        则counter继续加1,这期间全部其余线程都只能等待,等待该线程释放全部锁,即counter递减到0为止        死锁形成的问题.程序卡死        一个锁不会产生死锁        当有多个锁多个线程时会产生死锁        a b  锁        p k  线程        当p 和 k 都须要a和b锁时才可能产生死锁      递归锁(可重入锁)RLock        同一个线程能够屡次执行acquire  执行一次acquire  计数加1        执行一次release 次数减一 执行acquire的次数须要与release的次数对应        在执行被锁的代码时   同一个线程 不会判断次数   其余线程须要判断 计数为0才能够执行        不是用来解决死锁的      Semaphore信号量(了解) 经常使用在线程中        信号量做用:限制同时执行被锁代码的线程数量        案列:        sem = semaphore(2)        acquire        code.....        release        开了十个线程 只能有两个同时执行    八、队列queue(***)        queue 这个queue和进程里的Queue不一样 就是一个简单的容器            队列是一种数据的容器            特色:先进先出        queue先进先出        lifoqueue先进后出        priorityqueue  优先级队列  整型表示优先级   数字越大优先级越低        import queue        q = queue.Queue()# 普通队列 先进先出        q.put("a")        q2 = queue.LifoQueue()# 堆栈队列  先进后出 后进先出  函数调用就是进栈  函数结束就出栈 递归形成栈溢出        q3 = queue.PriorityQueue()  # 优先级队列    九、Event事件(**)了解    python线程的事件用于主线程控制其余线程的执行,事件主要提供了三个方法 set、wait、clear。    事件处理的机制:全局定义了一个“Flag”,若是“Flag”值为 False,        那么当程序执行 event.wait 方法时就会阻塞,若是“Flag”值为True,那么event.wait 方法时便再也不阻塞。        是什么?            线程间通信的方式        为何用?            简化代码        set()设置为True        wati()阻塞 直到为True        clear:将“Flag”设置为False四、池(*****)    就是一个装进程/线程的容器    为什么要用池:        操做系统没法无限开启进程或线程        池做用是将进程或线程控制操做系统可承受的范围内    何时用进程池: (好比 双十一)        当程序中有多个进程时 管理变得很是麻烦        进程池能够帮咱们管理进程            1.进程的建立            2.进程的销毁            3.任务的分配            4.限制最大的进程数 保证系统正常运行    池内装的东西有两种:            装进程:进程池            装线程:线程池    进程池        在利用Python进行系统管理的时候,特别是同时操做多个文件目录,或者远程控制多台主机,        并行操做能够节约大量的时间。多进程是实现并发的手段之一,须要注意的问题是:            1 很明显须要并发执行的任务一般要远大于核数            2 一个操做系统不可能无限开启进程,一般有几个核就开几个进程            3 进程开启过多,效率反而会降低(开启进程是须要占用系统资源的,并且开启多余核数目的进程也没法作到并行)        例如当被操做对象数目不大时,能够直接利用multiprocessing中的Process动态成生多个进程,        十几个还好,但若是是上百个,上千个。。。手动的去限制进程数量却又太过繁琐,此时能够发挥进程池的功效。        咱们就能够经过维护一个进程池来控制进程数目,好比httpd的进程模式,规定最小进程数和最大进程数...        建立进程池的类:若是指定numprocess为3,则进程池会从无到有建立三个进程,                    而后自始至终使用这三个进程去执行全部任务,不会开启其余进程    使用方式?        ThreadPoolExecutor  线程池            实例化 时指定最大线程数        ProcessPoolExecutor  进程池            实例化 时指定最大进程数        执行submit来提交任务        from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor        p=ThreadPoolExecutor(4) # 默认开启的线程数是cpu的核数*5        p.submit(task,i)    总结一下:        进程池能够自动建立进程        进程限制最大进程数        自动选择一个空闲的进程帮你处理任务    进程何时算是空闲?        代码执行完算是空闲    进程池,池子内何时装进程:并发的任务属于计算密集型    线程池,池子内何时装线程:并发的任务属于IO密集型回调函数:    须要回调函数的场景:进程池中任何一个任务一旦处理完了,就当即告知主进程:        我好了额,你能够处理个人结果了。主进程则调用一个函数去处理该结果,该函数即回调函数    咱们能够把耗时间(阻塞)的任务放到进程池中,而后指定回调函数(主进程负责执行),    这样主进程在执行回调函数时就省去了I/O的过程,直接拿到的是任务的结果。    若是在主进程中等待进程池中全部任务都执行完毕后,再统一处理结果,则无需回调函数
相关文章
相关标签/搜索