一:并发与并行:

  • 1:CPU交替处理多个任务,还是有两个程序,但是只有一个CPU,会交替处理这两个程序,而不是同时执行,只不过因为CPU执行的速度过快,而会使得人们感到是在“同时”执行,执行的先后取决于各个程序对于时间片资源的争夺。
  • 2:多个CPU同时执行多个任务,就好像有两个程序,这两个程序是真的在两个不同的CPU内同时被执行。

二:进程与线程:

  • 1:进程:操作系统资源分配的单位。
  • 2:线程:cpu调度的单位。
  • 3:线程可以共享全局变量,出现资源竞争问题,可以通过互斥锁和线程同步解决。

三:GIL锁的介绍:

1:引入GIL锁的原因?

答:
1:为了规避类似于内存管理这样的复杂的竞争风险问题:python的内存管理是依靠引用计数,例如:现存的a变量的引用计数是1, 在并行条件下,CUP1中运行着一个线程,引用了一次a,而CUP2中运行也运行着一个线程,同时引用了一次a,由于同时,内存中的引用计数,只会增加到2,而实际是3,这样内存就被污染了。

2:是因为 CPython 大量使用 C 语言库,但大部分 C 语言库都不是原生线程安全的(线程安全会降低性能和增加复杂度)。

2:GIL锁机制:

答:假设现在有三个线程,A线程正在运行。当A线程遇到IO操作,A线程释放GIL锁。此时 B, C线程就去抢GIL锁,谁抢到谁就能继续执行。或者是当A线程的Tick时间到期,此时A也要释放GIL锁,不同的是ABC三个线程都会抢夺GIL锁,谁抢到谁继续执行。

3:GIL锁导致无法利用多核?

答:是指的无法同时利用多核并行执行。多核CUP情况下,假设Thread1在CPU1上执行,Thread2在CPU2上执行,CPU2上的Thread2需要等待CPU1上的Thread1让出GIL锁,才有可能执行。如果在多次竞争中,Thread1都胜出,Thread2没有得到GIL锁,意味着CPU2一直是闲置的,无法发挥多核的优势。同时在这个CPU上执行就不能在那个CPU上执行,也就是同一时刻只能是利用一个CPU

为了避免同一线程霸占CPU,在python3.x中,线程会自动的调整自己的优先级,使得多线程任务执行效率更高。

四:面试问题:

1:有了GIL锁是否意味着线程就安全了?

答:不是,GIL锁解决的是是解释器层面的,他只能规避类似于内存管理这样的复杂的竞争风险问题,但是我们自己写的代码,还是需要我们自己使用互斥锁来解决

2: I/O密集型与计算密集型?

1、如果是CPU密集型代码(循环、计算等),由于计算工作量多和大,计算很快就会达到100,然后触发GIL的释放与在竞争,多个线程来回切换损耗资源,所以在多线程遇到CPU密集型代码时,单线程会比多线程的快。

2、如果是I\O密集型代码(文件处理、网络爬虫),开启多线程实际上是并发(不是并行),IO操作会进行IO等待,线程A等待时,自动切换到线程B,这样就提升了效率,比单线程快很多

3:如何改善GIL锁带来的问题?

1:为了避免同一线程霸占CPU,在python3.x中,线程会自动的调整自己的优先级,使得多线程任务执行效率更高。
2:Python程序如果是计算密集型的,使用python多进程或者协程替代多线程。
3:GIL只存在于CPython解释器上,我们可以更换其他解释器,比如Jython(用JAVA实现的)。

4:GIL锁导致多核多线程比单核多线程更差?

原因是单核下多线程,每次释放GIL,唤醒的那个线程都能获取到GIL锁,所以能够无缝执行,但多核下,CPU0释放GIL后,其他CPU上的线程都会进行竞争,但GIL可能会马上又被CPU0拿到,导致其他几个CPU上被唤醒后的线程会醒着等待到切换时间后又进入待调度状态,这样会造成线程颠簸(thrashing),导致效率更低。