python函数关系图 python绘制关系图( 八 )


由于GIL的存在 , 即使是多线程,事实上同一时刻只能保证一个线程在运行,既然这样多线程的运行效率不就和单线程一样了吗,那为什么还要使用多线程呢?
由于以前的电脑基本都是单核CPU,多线程和单线程几乎看不出差别 , 可是由于计算机的迅速发展,现在的电脑几乎都是多核CPU了,最少也是两个核心数的,这时差别就出来了:通过之前的案例我们已经知道,即使在多核CPU中,多线程同一时刻也只有一个线程在运行 , 这样不仅不能利用多核CPU的优势,反而由于每个线程在多个CPU上是交替执行的,导致在不同CPU上切换时造成资源的浪费,反而会更慢 。即原因是一个进程只存在一把gil锁,当在执行多个线程时,内部会争抢gil锁,这会造成当某一个线程没有抢到锁的时候会让cpu等待,进而不能合理利用多核cpu资源 。
但是在使用多线程抓取网页内容时,遇到IO阻塞时 , 正在执行的线程会暂时释放GIL锁,这时其它线程会利用这个空隙时间,执行自己的代码,因此多线程抓取比单线程抓取性能要好,所以我们还是要使用多线程的 。
GIL对多线程Python程序的影响
程序的性能受到计算密集型(CPU)的程序限制和I/O密集型的程序限制影响,那什么是计算密集型和I/O密集型程序呢?
计算密集型:要进行大量的数值计算 , 例如进行上亿的数字计算、计算圆周率、对视频进行高清解码等等 。这种计算密集型任务虽然也可以用多任务完成,但是花费的主要时间在任务切换的时间 , 此时CPU执行任务的效率比较低 。
IO密集型:涉及到网络请求(time.sleep())、磁盘IO的任务都是IO密集型任务,这类任务的特点是CPU消耗很少 , 任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度) 。对于IO密集型任务,任务越多,CPU效率越高 , 但也有一个限度 。
当然为了避免GIL对我们程序产生影响,我们也可以使用 , 线程锁 。
LockRLock
常用的资源共享锁机制:有Lock、RLock、Semphore、Condition等,简单给大家分享下Lock和RLock 。
Lock
特点就是执行速度慢 , 但是保证了数据的安全性
RLock
使用锁代码操作不当就会产生死锁的情况 。
什么是死锁
死锁:当线程A持有独占锁a,并尝试去获取独占锁b的同时,线程B持有独占锁b,并尝试获取独占锁a的情况下,就会发生AB两个线程由于互相持有对方需要的锁,而发生的阻塞现象,我们称为死锁 。即死锁是指多个进程因竞争资源而造成的一种僵局,若无外力作用 , 这些进程都将无法向前推进 。
所以,在系统设计、进程调度等方面注意如何不让这四个必要条件成立,如何确定资源的合理分配算法,避免进程永久占据系统资源 。
死锁代码
python线程间通信
如果各个线程之间各干各的,确实不需要通信,这样的代码也十分的简单 。但这一般是不可能的,至少线程要和主线程进行通信,不然计算结果等内容无法取回 。而实际情况中要复杂的多 , 多个线程间需要交换数据,才能得到正确的执行结果 。
python中Queue是消息队列,提供线程间通信机制,python3中重名为为queue,queue模块块下提供了几个阻塞队列,这些队列主要用于实现线程通信 。
在 queue 模块下主要提供了三个类,分别代表三种队列,它们的主要区别就在于进队列、出队列的不同 。
简单代码演示
此时代码会阻塞,因为queue中内容已满,此时可以在第四个queue.put('苹果')后面添加timeout,则成为 queue.put('苹果',timeout=1)如果等待1秒钟仍然是满的就会抛出异常,可以捕获异常 。

推荐阅读