zoukankan      html  css  js  c++  java
  • 并发编程之全局解释器锁(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解释器同一时间只能执行一个任务的代码

    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
  • 相关阅读:
    win10 访问共享缺少 SMB1协议
    H3C 设置用户和密码
    vim 注释和删除多行
    工商银行贵金属网址
    Team Foundation Server 2008 安装 全程记录
    临时表和表变量 区别
    Windows Server 2003开机自动登录
    索引工作原理
    WebService是什么
    CodeFirst
  • 原文地址:https://www.cnblogs.com/featherwit/p/13387542.html
Copyright © 2011-2022 走看看