并发编程之全局解释器锁(GIL)

GIL介绍

GIL的定义

GIL的官方定义如下:

In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple 
native threads from executing Python bytecodes at once. This lock is necessary mainly 
because CPython’s memory management is not thread-safe. (However, since the GIL 
exists, other features have grown to depend on the guarantees that it enforces.)

在CPython解释器中GI是一把互斥锁, 用来阻止同一个进程下的多个线程同时执行。因为CPython中的内存管理不是线程安全的。即垃圾回收机制不是线程安全的。

Python解释器有很多个版本: CPython、JPython、PyPython。但是普遍使用的都是CPython。

由定义, 可以得出

  • GIL不是Python的特点, 而是CPython解释器的特点
  • GIL是保证解释器级别的数据安全
  • GIL会导致同一个进程下的多个线程无法同时执行即无法利用多核优势
  • 针对不同的数据还是需要加不同的锁处理

GIL的介绍

GIL本质就是一把互斥锁,既然是互斥锁,所有互斥锁的本质都一样,都是将并发运行变成串行,以此来控制同一时间内共享数据只能被一个任务所修改,进而保证数据安全。

在一个Python的进程内,不仅有py文件运行的主线程或者由该主线程开启的其他线程,还有解释器开启的垃圾回收等解释器级别的线程,总之,所有线程都运行在这一个进程内

1 所有数据都是共享的,这其中,代码作为一种数据也是被所有线程共享的

2 所有线程的任务,都需要将任务的代码当做参数传给解释器的代码去执行,即所有的线程要想运行自己的任务,首先需要解决的是能够访问到解释器的代码。

那么多线程的执行流程为:

多个线程先访问到解释器的代码,即拿到执行权限,然后将target的代码交给解释器的代码去执行

解释器的代码是所有线程共享的,所以垃圾回收线程也可能访问到解释器的代码而去执行,这就导致了一个问题:对于同一个数据100,可能线程1执行x=100的同时,而垃圾回收执行的是回收100的操作,解决这种问题没有什么高明的方法,就是加锁处理,如下图的GIL,保证python解释器同一时间只能执行一个任务的代码

扫描二维码关注公众号,回复: 11455347 查看本文章

GIL与Lock

GIL保护的是解释器级的数据,保护用户自己的数据则需要自己加锁处理

多线程的应用场景

有了GIL的存在,同一时刻同一进程中只有一个线程被执行。那么同一个进程下的多线程无法利用多核优势, 是不是就没有用了? 这还是需要看具体情况的

现在的电脑基本上都是多核的, 所以暂不考虑单核的问题。

对于多核计算机来说, 对于计算密集型的, 使用多进程可以并行计算, 节约时间。对于I/O密集型的, 使用多线程能节省资源。

验证多进程与多线程的应用场景

多进程

计算密集型

def work():
    res = 0
    for i in range(1000000):
        res *= i


if __name__ == '__main__':
    p_list = []
    print(os.cpu_count())  # 获取当前计算机CPU个数
    process_start_time = time.time()
    for i in range(12):
        p = Process(target=work)
        p.start()
        p_list.append(p)
    for p in p_list:
        p.join()
    print('多进程的运行时间为:', time.time() - process_start_time) #  多进程的运行时间为: 0.09932708740234375

    t_list = []
    thread_start_time = time.time()
    for i in range(12):
        t = Thread(target=work)
        t.start()
        t_list.append(t)

    for t in t_list:
        t.join()

    print('多线程的运行时间为:', time.time() - thread_start_time)  # 多线程的运行时间为: 0.4195849895477295

多线程

IO密集型

def work():
    time.sleep(2)


if __name__ == '__main__':
    p_list = []
    print(os.cpu_count())  # 获取当前计算机CPU个数
    process_start_time = time.time()
    for i in range(4000):
        p = Process(target=work)
        p.start()
        p_list.append(p)
    for p in p_list:
        p.join()
    print('多进程的运行时间为:', time.time() - process_start_time)  # 多进程的运行时间为: 20.892397165298462

    t_list = []
    thread_start_time = time.time()
    for i in range(4000):
        t = Thread(target=work)
        t.start()
        t_list.append(t)

    for t in t_list:
        t.join()

    print('多线程的运行时间为:', time.time() - thread_start_time)  # 多线程的运行时间为: 2.7849910259246826

猜你喜欢

转载自www.cnblogs.com/featherwit/p/13387542.html