002_Python多线程至关于单核多线程的论证

不少人都说python多线程是假的多线程!下面进行论证解释:java

1、python

  咱们先明确一个概念,全局解释器锁(GIL)
  Python代码的执行由Python虚拟机(解释器)来控制。Python在设计之初就考虑要在主循环中,同时只有一个线程在执行,就像单CPU的系统中运行多个进程那样,内存中能够存放多个程序,但任意时刻,只有一个程序在CPU中运行。一样地,虽然Python解释器能够运行多个线程,只有一个线程在解释器中运行。网络

  对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同时只有一个线程在运行。在多线程环境中,Python虚拟机按照如下方式执行。
    1.设置GIL。
    2.切换到一个线程去执行。
    3.运行。
    4.把线程设置为睡眠状态。
    5.解锁GIL。
    6.再次重复以上步骤。
  对全部面向I/O的(会调用内建的操做系统C代码的)程序来讲,GIL会在这个I/O调用以前被释放,以容许其余线程在这个线程等待I/O的时候运行。若是某线程并未使用不少I/O操做,它会在本身的时间片内一直占用处理器和GIL。也就是说,I/O密集型的Python程序比计算密集型的Python程序更能充分利用多线程的好处。  多线程

  咱们都知道,比方我有一个4核的CPU,那么这样一来,在单位时间内每一个核只能跑一个线程,而后时间片轮转切换。可是Python不同,它无论你有几个核,单位时间多个核只能跑一个线程,而后时间片轮转。看起来很难以想象?可是这就是GIL搞的鬼。任何Python线程执行前,必须先得到GIL锁,而后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把全部线程的执行代码都给上了锁,因此,多线程在Python中只能交替执行,即便100个线程跑在100核CPU上,也只能用到1个核。一般咱们用的解释器是官方实现的CPython,要真正利用多核,除非重写一个不带GIL的解释器。
  咱们不妨作个试验:ide

#coding=utf-8
from threading import Thread

def loop():
    while True:
        pass

if __name__ == '__main__':

    for i in range(3):
        t = Thread(target=loop)
        t.start()

    while True:
        pass

Windows状态以下:oop

Mac状态以下:spa

  咱们发现CPU利用率并无占满,大体至关于单核水平。
  而若是咱们变成进程呢?
  咱们改一下代码:操作系统

#coding=utf-8
from multiprocessing import Process

def loop():
    while True:
        pass

if __name__ == '__main__':

    for i in range(3):
        t = Process(target=loop)
        t.start()

    while True:
        pass

 Windows状态以下:线程

Mac状态以下:设计

 

  结果直接飙到了100%,说明进程是能够利用多核的!
  为了验证这是Python中的GIL搞得鬼,我试着用Java写相同的代码,开启线程,咱们观察一下:

 

package com.darrenchan.thread;

public class TestThread {
    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    while (true) {

                    }
                }
            }).start();
        }
        while(true){

        }
    }
}

 效果以下:

  因而可知,Java中的多线程是能够利用多核的,这是真正的多线程!而Python中的多线程只能利用单核,这是假的多线程!

  难道就如此?咱们没有办法在Python中利用多核?固然能够!刚才的多进程算是一种解决方案,还有一种就是调用C语言的连接库。对全部面向I/O的(会调用内建的操做系统C代码的)程序来讲,GIL会在这个I/O调用以前被释放,以容许其余线程在这个线程等待I/O的时候运行。咱们能够把一些 计算密集型任务用C语言编写,而后把.so连接库内容加载到Python中,由于执行C代码,GIL锁会释放,这样一来,就能够作到每一个核都跑一个线程的目的!

  可能有的小伙伴不太理解什么是计算密集型任务,什么是I/O密集型任务?

  计算密集型任务的特色是要进行大量的计算,消耗CPU资源,好比计算圆周率、对视频进行高清解码等等,全靠CPU的运算能力。这种计算密集型任务虽然也能够用多任务完成,可是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低,因此,要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数。

  计算密集型任务因为主要消耗CPU资源,所以,代码运行效率相当重要。Python这样的脚本语言运行效率很低,彻底不适合计算密集型任务。对于计算密集型任务,最好用C语言编写。

第二种任务的类型是IO密集型,涉及到网络、磁盘IO的任务都是IO密集型任务,这类任务的特色是CPU消耗不多,任务的大部分时间都在等待IO操做完成(由于IO的速度远远低于CPU和内存的速度)。对于IO密集型任务,任务越多,CPU效率越高,但也有一个限度。常见的大部分任务都是IO密集型任务,好比Web应用。

  IO密集型任务执行期间,99%的时间都花在IO上,花在CPU上的时间不多,所以,用运行速度极快的C语言替换用Python这样运行速度极低的脚本语言,彻底没法提高运行效率。对于IO密集型任务,最合适的语言就是开发效率最高(代码量最少)的语言,脚本语言是首选,C语言最差。  综上,Python多线程至关于单核多线程,多线程有两个好处:CPU并行,IO并行,单核多线程至关于自断一臂。因此,在Python中,可使用多线程,但不要期望能有效利用多核。若是必定要经过多线程利用多核,那只能经过C扩展来实现,不过这样就失去了Python简单易用的特色。不过,也不用过于担忧,Python虽然不能利用多线程实现多核任务,但能够经过多进程实现多核任务。多个Python进程有各自独立的GIL锁,互不影响。

相关文章
相关标签/搜索