GIL锁定义

GIL锁:Global Interpreter Lock  全局解释器

本质上是一把互斥锁

官方解释:

  在CPython中,这个全局解释器锁,也称为GIL,是一个互斥锁,防止多个线程在同一时间执行Python字节码,

  这个锁是非常重要的,因为CPython的内存管理是非线程安全的,也有很多其他的特性依赖于GIL(比如有些

  东西是依赖这个GIL写好的,要替换成本很高),所以即使它影响了程序的效率,也无法直接将其去除。

需要知道的是,解释器并不只有CPython,还有PyPy,Jpython等等,GIL仅存在于Cpython中,这并不是Python

这们语言的问题,而是CPython解释器的问题

GIL解决的问题

执行py文件的三个步骤

  1.从硬盘加载python解释器到内存

  2.从硬盘加载py文件到内存

  3.解释器解析py文件内容,交给cpu执行

并且,每当执行一个py文件,就会立即启动一个python解释器

解释器的作用

  py文件中的内容本质都是字符串,cpu无法执行,只有当字符串被解释器解释成python字节码的时候,

  才能被cpu执行起来,所以,对于一个进程来说,同时只能有一个线程被解释器执行

内存管理机制

  对于python而言,我们是不需要手动去清除垃圾的,因为python中有一个垃圾回收机制

  垃圾回收机制的原理是依据引用计数

  a = 10  内存中10的地址计数为1

  b = a     10的计数变为2

  b = 1     10的计数变为1

  a = 1     10的计数变为0

  当垃圾回收启动后,会将计数为0的数据清除掉,回收内存(占着内存没有计数的称为内存泄漏)

产生的问题

  垃圾回收机制本质上也是一串代码,也是需要被解释器解释执行

  所以对于一个开启的进程而言

  不仅有你执行py文件的一个线程,还有着向垃圾回收机制这样的线程

  当多个线程同时并发执行的时候,就会产生一个问题,就是两条线程同时访问统一资源带来的数据错乱问题

  例如:

    主线程定义一个变量,刚使用解释器被cpu执行到开辟空间的时候,引用计数还没有变成1,然后cpu切走了

    然后垃圾回收线程被唤醒了,并且解释器切换到它使用,他一看内存空间有引用计数为0的,就清除了

    当主线程再切回来的时候就懵逼了

GIL如何解决

  GIL会给解释器上一把锁,当一条线程使用解释器的时候,其他线程无法使用解释器,

  解释器会在cpu切走之前,切换线程,并且会保存上一个线程的一些状态,保证不会被其他线程修改

  这样就让一个系统资源的一个安全性得到保障

GIL带来的问题

问题

  由于这个GIL的锁的问题,就导致在同一个进程中,多个线程只能并发执行,并不能做到并行执行

  所以在计算密集型的问题上,CPython的速度要慢于其他高级编程

为什么要这么设计

  CPython诞生于1991年,多核处理器诞生于2004年,

  所以在CPython诞生的时候,本来就不行并行执行,所以在当时加锁的处理是基本完美的

  为什么拿掉这个锁的设计,在这个期间,很多已经完成的代码都是依赖GIL锁完成的

  如果直接拿掉,代码得重新修改,成本太大

GIL锁的加锁与解锁时机

加锁:当一个线程使用解释器时,就立马加锁

释放:

  1、该线程任务结束

  2、该线程遇到IO操作

  3、该线程使用解释器过程  默认100纳秒(一般比cpu的时间片短)

关于GIL的性能讨论

GIL的优点:

  保证了CPython中的内存管理是线程安全的

GIL的缺点:

  互斥锁的特性使得多线程无法并行

但我们并不能因此否认Python这门语言,原因如下:

  1、GIL仅仅在CPython解释器中存在,在其他解释器中没有,并不是Python这门语言的缺点

  2、在单核处理器下,多线程之间本来就无法真正的并行执行

  3、在多核处理器下,运算效率的却是比单核处理器高,但要知道现代应用程序多数都是基于网络的

   CPU无法决定网络速度的,当IO操作比较多的时候,多核也需要等待IO操作完成,优势就没那么明显了

总结:

  1、单核状态下,无论是IO密集还是计算密集型GIL都不会产生任何影响(本来就只能并发)

  2、多核情况下对于IO密集型任务,GIL会有细微的影响,基本可以忽略

  3、CPython中IO密集任务应该采用多线程,计算密集型应该采用多进程

自定义的线程锁与GIL的区别

相同点:都是互斥锁

不同点:GIL锁锁的是解释器内部的资源,例如引用计数,分带回收数据等等,

    但并不能保证我们自定义数据资源的安全性

    所以我们自己开启的共享资源还得自己加锁,保证资源的安全

进程池与线程池

池  就是容器,

进程池:装进程的容器

线程池:装线程的容器

好处

  1、自动管理线程的开启和销毁

  2、自动分配任务给空闲的线程

  3、可以限制开启线程的数量,保证系统的稳定

     在这里需要注意的是:这里的限制数量和信号量中的控制并发数量不一样,这里限制的是开启

     线程的最大数量(线程可能还没开启),但是信号量中的线程已经全部开启,控制并发量

如何使用

  1、创建池子

  2、submit提交任务

  3、pool.shutdown() # 等待所有任务全部完毕,销毁所有线程后关闭线程池(主线程需要等待)

from concurrent.futures import ThreadPoolExecutor   # 导入类

pool = ThreadPoolExecutor()  # 实例化产生池子对象

def task(name):
# 代码区
print(name)
pass pool.submit(task,"rock") pool.shutdown() # 可以让主线程等待线程池里的任务全部完毕,才往下执行

同步异步

在并发编程中,经常提及到的名词:阻塞非阻塞,并行并发,同步异步

阻塞非阻塞

  指的是程序的运行状态,程序运行有三个状态:阻塞态、运行态、就绪态

  阻塞指的是阻塞态  非阻塞可能是运行态或者是就绪态

并发并行

  指的是多任务状态下处理任务的方式

  并发:多个任务看起来像在同时运行,本质上是切换+保存状态

  并行:真正的同时执行,必须具备多核处理器

同步异步

  指的是任务提交的方式

  同步:指的是任务发起后,必须在原地等待任务完成,拿到结果,才能接着往下执行,

     这就称之为同步,默认的情况就是同步

     注意:协程也是同步

  异步:指的是任务发起后,不需要再原地等待,可以接着执行其他代码,称之为异步

    异步任务必须依赖并发或者并行  再python中通过多进程、多线程实现

    异步可以一起执行代码,效率明显高于同步

需要注意的是:

  1、同步不等于阻塞:同步虽然需要在原地等待,但是提交的任务可能一直在运行,那就不是阻塞

  2、卡住不等于阻塞:原因同上

  3、异步不等于非阻塞:提交完任务后,如果两边都遇到了IO操作,那就阻塞了

异步回调

异步指的是任务提交的方式是异步的

异步出现的问题

  如果这个任务需要返回值,我需要拿到返回值处理结果,那么该什么时候去拿返回值

  如果早了,任务还未完成;如果晚了,那么结果就没有得到及时处理

解决方案

  异步回调

异步回调指的就是一个函数,该函数会在任务完成后自动被调用,并且会把一个Future对象传进去(任务对象)

通过Future对象的result()获取执行结果

import time
from concurrent.futures import ThreadPoolExecutor # 导入类 pool = ThreadPoolExecutor() # 实例化产生池子对象 def task(): # 任务函数
print("task run")
time.sleep(2)
print("task over")
return "执行结果" # 返回结果 def call_back(obj): # 回调函数,调用会自动把Future对象传进去,所以在这需要接收一下
res = obj.result() # 通过result() 拿到结果
print(res) p = pool.submit(task) # 创建一个Future对象 <Future at 0x200d73230f0 state=running>
p.add_done_callback(call_back) # Future对象有个方法添加回调函数
# 这样提交完任务后不仅会被及时处理,而且还不需要等待,可以接着执行其他代码

通常异步任务都会绑定一个回调函数,用来处理任务结果

在进程池中,回调函数是在父进程中执行的,进程间数据不共享,需要把数据返回来

在线程池中,回调函数是由当前执行的线程发起执行的,同一进程中的线程共享资源,可以直接运行

GIL锁、进程池与线程池、同步异步的更多相关文章

  1. GIL锁、进程池与线程池

    1.什么是GIL? 官方解释: ''' In CPython, the global interpreter lock, or GIL, is a mutex that prevents multip ...

  2. GIL解释锁及进程池和线程池

    官方介绍 ''' 定义: In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple nati ...

  3. day37 GIL、同步、异步、进程池、线程池、回调函数

    1.GIL 定义: GIL:全局解释器锁(Global Interpreter Lock) 全局解释器锁是一种互斥锁,其锁住的代码是全局解释器中的代码 为什么需要全局解释器锁 在我们进行代码编写时,实 ...

  4. GIL全局解释器锁、死锁现象、python多线程的用处、进程池与线程池理论

    昨日内容回顾 僵尸进程与孤儿进程 # 僵尸进程: 所有的进程在运行结束之后并不会立刻销毁(父进程需要获取该进程的资源) # 孤儿进程: 子进程正常运行 但是产生该子进程的父进程意外死亡 # 守护进程: ...

  5. GIL解释器锁 & 进程池与线程池

    今日内容 GIL 全局解释器锁(重要理论) 验证 GIL 的存在及功能 验证 python 多线程是否有用 死锁现象 进程池与线程池(使用频率高) IO模型 详细参考: https://www.bil ...

  6. 4月27日 python学习总结 GIL、进程池、线程池、同步、异步、阻塞、非阻塞

    一.GIL:全局解释器锁 1 .GIL:全局解释器锁 GIL本质就是一把互斥锁,是夹在解释器身上的, 同一个进程内的所有线程都需要先抢到GIL锁,才能执行解释器代码 2.GIL的优缺点: 优点:  保 ...

  7. GIL与普通互斥锁区别,死锁现象,信号量,event事件,进程池与线程池,协程

    GIL与普通互斥锁区别 GIL锁和互斥锁的异同点 相同: 都是为了解决解释器中多个线程资源竞争的问题 异: 1.互斥锁是Python代码层面的锁,解决Python程序中多线程共享资源的问题(线程数据共 ...

  8. -1-5 java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait(),notify(),notifyAll()等方法都定义在Object类中

     本文关键词: java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁  sleep()和wait()方法的区别 为什么wait( ...

  9. GIL,queue,进程池与线程池

    GIL 1.什么是GIL(这是Cpython解释器) GIL本质就是一把互斥锁,既然是互斥锁,原理都是一样的,都是让多个并发线程同一时间只能有一个执行 即:有了GIL的存在,同一进程内的多个线程同一时 ...

随机推荐

  1. python中 将数字转化为人民币的形式

    def fn(args): """ 将金额转化为人民币模式,带逗号分隔,保留小数点两位,四舍五入 :param args: :return: ""&q ...

  2. PAT 甲级 1036 Boys vs Girls (25 分)(简单题)

    1036 Boys vs Girls (25 分)   This time you are asked to tell the difference between the lowest grade ...

  3. Python简单计算数组元素平均值的方法示例

    Python简单计算数组元素平均值的方法示例 本文实例讲述了Python简单计算数组元素平均值的方法.分享给大家供大家参考,具体如下: Python 环境:Python 2.7.12 x64 IDE ...

  4. git 命令使用

    //快速删除node_modules: 1.npm install -g rimraf  2. rimraf node_modules 1.本地新建分支并且连接远端 克隆:git clone 远端地址 ...

  5. Python基础——numpy库的使用

    1.numpy库简介:    NumPy提供了许多高级的数值编程工具,如:矩阵数据类型.矢量处理,以及精密的运算库.专为进行严格的数字处理而产生. 2.numpy库使用: 注:由于深度学习中存在大量的 ...

  6. LC 33. Search in Rotated Sorted Array

    问题描述 Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand. ...

  7. 切片(Slice)

    Python提供了切片(Slice)操作符:可以一次取出多个列表元素 L[0:3]表示,从索引0开始取,直到索引3为止,但不包括索引3.0可以省略:L[:3] L[:]:就是整个列表   补充: 前1 ...

  8. Python笔记day20-面向对象

    目录 面向对象 1 装饰器 1.1 装饰器是什么? 1.2 装饰器 2 面向对象 (Object Oriented) 简称OO 2.1 面向对象相关术语 2.2 类和对象 2.3 类和对象的实现和书写 ...

  9. Python反射和内置方法(双下方法)

    Python反射和内置方法(双下方法) 一.反射 什么是反射 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问.检测和修改它本身状态或行为的一种能力(自省).这一概念的提出很快引发 ...

  10. recover函数捕获异常

    package main import ( //"fmt" "time" ) func test () { var m map[string]int m[&qu ...