今日内容概要
1.死锁与递归锁
2.线程队列Queue
3.进程池和线程池
4.协程
5.greenlet模块
6.gevent模块
今日内容详情
-
死锁与递归锁
import time def eat1(lock1,lock2,name): lock1.acquire() print('%s谁抢到了筷子'%name) time.sleep(1) lock2.acquire() print('%s:抢到面条'%name) time.sleep(1) print('开始吃') time.sleep(1) lock2.release() print('面条放下了') lock1.release() def eat2(lock1,lock2,name): lock1.acquire() print('%s谁抢到了筷子' % name) time.sleep(1) lock2.acquire() print('%s:抢到面条' % name) time.sleep(1) print('开始吃') time.sleep(1) lock2.release() print('面条放下了') lock1.release() 死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象, 若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁, 这些永远在互相等待的进程称为死锁进程,如下就是死锁 from threading import Thread,Lock if __name__ == '__main__': lock1=Lock() lock2=Lock() for i in ['wkj','www','WKJ']: t=Thread(target=eat1,args=(lock1,lock2,i)) t.start() for i in ['QQQ','EEE','FFF']: t=Thread(target=eat2,args=(lock1,lock2,i)) t.start() 解决方法:递归锁,在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。 递归锁: from threading import Thread,RLock if __name__ == '__main__': lock1=RLock() lock2=lock1 for i in ['wkj','www','WKJ']: t=Thread(target=eat1,args=(lock1,lock2,i)) t.start() for i in ['QQQ','EEE','FFF']: t=Thread(target=eat2,args=(lock1,lock2,i)) t.start()
-
线程队列Queue
线程队列: 1.先进先出 2.后进先出 3.优先级队列 from queue import Queue, LifoQueue, PriorityQueue if __name__ == '__main__': # 1.先进先出 q = Queue() q.put('ly is handsome1') q.put('ly is handsome2') print(q.get()) # 2. 后进先出 堆, 栈。, 队列, 链表, q = LifoQueue() q.put('ly is handsome1') q.put('ly is handsome2') print(q.get()) # 3.优先级队列 q = PriorityQueue() q.put((20, 'a')) q.put((10, 'b')) q.put((30, 'c')) print(q.get())
-
进程池和线程池
ThreadPoolExecutor:线程池,提供异步调用 ProcessPoolExecutor:进程池,提供异步调用 基本方法: submit(fn, *args, **kwargs):异步提交任务 map(func, *iterables, timeout=None, chunksize=1):取代for循环submit的操作 shutdown(wait=True):相当于进程池的pool.close()+pool.join()操作 wait=True,等待池内所有任务执行完毕回收完资源后才继续 wait=False,立即返回,并不会等待池内的任务执行完毕 但不管wait参数为何值,整个程序都会等到所有任务执行完毕 submit和map必须在shutdown之前 result(timeout=None):取得结果 add_done_callback(fn):回调函数 done():判断某一个线程是否完成 cancle():取消某个任务 from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor def task(n): time.sleep(1) return n + n def call_back(res): print(res.result()) if __name__ == '__main__': urls = [1, 2, 3, 4] p1 = ProcessPoolExecutor(3) # 里面有三个进程 p2 = ThreadPoolExecutor(3) # 里面有三个线程 for url in urls: p1.submit(task, url).add_done_callback(call_back) for url in urls: p2.submit(task, url).add_done_callback(call_back) print('ZJC')
-
协程
协程:是一种用户态的轻量级线程,由用户程序自己控制调度得的 强调: 1.python的线程属于内核级别的,即由操作系统控制调度 (如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行) 2.单线程内开启协程,一旦遇到io,就会从应用程序级别 (而非操作系统)控制切换,以此来提升效率 (!!!非io操作的切换与效率无关) 优点: 1.协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级 2.单线程内就可以实现并发效果,最大限度地利用cpu 缺点: 1.协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程, 每个进程内开启多个线程,每个线程内开启协程 2.协程是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程 特点: 1.必须在只有一个单线程里实现并发 2.修改共享数据不需加锁 3.用户程序里自己保存多个控制流的上下文栈 4.一个协程遇到IO操作自动切换到其它协程
-
greenlet模块
安装:pip3 install greenlet greenlet只是提供了一种比generator更加便捷的切换方式,当切到一个任务执行时如果遇到io,那就原地阻塞,仍然是没有解决遇到IO自动切换来提升效率的问题。 单线程里的这20个任务的代码通常会既有计算操作又有阻塞操作,我们完全可以在执行任务1时遇到阻塞,就利用阻塞的时间去执行任务2…如此,才能提高效率,这就用到了Gevent模块 from greenlet import greenlet def eat(name): print('%s:吃了一口'%name) g2.switch(name) print('%s:又吃了一口'%name) g2.switch() def play(name): print('%s:玩了一下' % name) g1.switch() print('%s:又玩了一下' % name) g1=greenlet(eat) g2=greenlet(play) g1.switch('wkj')
-
gevent模块
安装:pip3 install gevent gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度 import time import gevent def eat(name): print('%s:吃了一口'%name) gevent.sleep(2) print('%s:又吃了一口'%name) def play(name): print('%s:玩了一下' % name) gevent.sleep(3) print('%s:又玩了一下' % name) ctime=time.time() g1=gevent.spawn(eat,'wkj') g2=gevent.spawn(play,'wkj') g1.join() g2.join() print(time.time()-ctime) 猴子补丁 什么是猴子补丁? 1,这个词原来为Guerrilla Patch,杂牌军、游击队,说明这部分不是原装的,在英文里guerilla发音和gorllia(猩猩)相似,再后来就写了monkey(猴子)。 2,还有一种解释是说由于这种方式将原来的代码弄乱了(messing with it),在英文里叫monkeying about(顽皮的),所以叫做Monkey Patch。 名字听起来稀奇古怪的, 跟python的这个功能搭不上边。 拥有在模块运行时替换的功能, 例如: 一个函数对象赋值给另外一个函数对象(把函数原本的执行的功能给替换了) 这里有一个比较实用的例子,很多用到import json, 后来发现ujson性能更高,如果觉得把每个文件的import json改成import ujson as json成本较高, 或者说想测试一下ujson替换是否符合预期, 只需要在入口加上: import json import ujson def monkey_patch_json(): json.__name__ = 'ujson' json.dumps = ujson.dumps json.loads = ujson.loads monkey_patch_json() aa=json.dumps({'name':'wkj','age':22}) print(aa)