GIL全局解释锁
定义
GIL锁:Global Interpreter Lock,又称为全局解释锁。
任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。GIL锁实际上把所有线程的执行代码都给上了锁。所以,多线程在Python中智能交替执行,即使100个线程跑在100核CPU上也只能用到一核。
GIL锁不是Python特性
GIL是Python解释器(Cpython)时引入的概念,在JPython、PyPy中没有GIL.GIL并不是Python的语言缺陷。是解释器层级的锁,跟Python语言特性无关
GIL存在原因
- 早期计算机都是单核设计
- CPython在执行多线程时并不是线程安全的,所以为了程序的稳定性,加一把全局解释锁,能够确保任何时候都只有宇哥Python线程执行
GIL的弊端
- 对计算密集型的程序产生影响,因为计算密集型的程序,需要占用系统资源
- GIL的存在,相当于始终在进行单线程运算
- IO密集型影响不大的原因在于,IO,input、output,这两个词就表明程序的瓶颈在于输入所耗费的时间,线程大部分时间在等待
GIL锁释放
- 在当前线程执行超时后会自动释放
- 在当前线程指向阻塞操作时会自动释放
- 当前执行完成时
GIL解决方案
由于GIL锁的原因,导致多线程并不能很好地利用CPU资源,所以需要解决GIL锁的问题
-
换解释器
-
使用多进程替换多线程
-
使用Python特性:胶水,让子线程用c语言来写
- 将c语言写的子线程编译为.so文件
- 使用ctypes加载.so文件
- 开启多线程
Python中的可变和不可变
可变与不可变是指内存中的那块内容是否可以被改变
-
可变类型(mutable),创建后可以继续修改对象的内容,如字典、列表
-
不可变类型(unmutable),一旦创建不可修改内容,如数字、字符串、元组
验证:
# # 数值
# a = 5
# print('a的值为:', a, '\na的内存地址为:', id(a))
# # 改变后内存会重新赋予一个内存地址
# a = 5 + 1
# print('a的值为:', a, '\na的内存地址为:', id(a))
#
# # 字符串
# b = 'hahazhong'
# print('b的值为:', b, '\nb的内存地址为:', id(b))
# # 改变后内存会重新赋予一个内存地址
# b = 'hahazhong' + '120'
# print('改变后b的值为:', b, '\nb的内存地址为:', id(b))
def test(a, b):
c = id(a)
print(f'------{
type(a)}------------')
print(f'原来的值为: {
a} \n内存地址为: {
id(a)}')
# 改变后内存会重新赋予一个内存地址
a += b
print(f'改变后的值为:{
a} \n内存地址为: {
id(a)}')
if id(a) == c:
print('内存地址未改变')
else:
print('内存地址改变')
# 数值
a = 5
test(a, a)
# 字符串
b = '哈哈钟'
test(b, b)
# 元组
c = (1, 2, 3, 4)
test(c, c)
# 列表
d = [1, 2, 3]
test(d, d)
# 字典
e = {
'1': 2, '3': 4}
f = {
'5': 6}
print(f'------{
type(e)}------------')
print(f'原来的值为: {
e} \n内存地址为: {
id(e)}')
e.update(f)
print(f'改变后的值为: {
e} \n内存地址为: {
id(e)}')
深浅拷贝
python中的copy模块,可以实现拷贝功能
-
浅拷贝
引用(地址)拷贝,并没有产生新的空间。如果拷贝的是对象,原对象和copy对象都指向同一个内存空间,只拷贝父对象,不会拷贝对象的内部的子对象。
用法:
copy.copy(变量)
进行拷贝 -
深拷贝
会产生新的空间.如果拷贝的是对象,原对象和copy都指向不同的内存空间,会拷贝对象及其子对象(产生新的空间)
用法:
copy.deepcopy(变量名)
作用:
- 减少内存的使用
- 以后在做数据的清洗、修改或者入库时,对源数据进行复制一份,以防数据修改后找不到源数据
简单可变类型深浅拷贝
# @object : 可变类型深浅拷贝都会产生新的空间,通过对列表进想赋值和深浅拷贝熟悉深浅拷贝
import copy
list1 = [1, 2, 3]
print('list1的内存地址为:', id(list1))
print('--' * 10)
# 赋值
# 赋值后list4的值修改list1也会改变,无法保证数据独立性
list4 = list1
print('list4的内存地址为:', id(list4))
print('--' * 10)
list4.append(77)
print(list1, 'list1的内存地址为:', id(list1))
print(list4, 'list4的内存地址为:', id(list4))
print('--' * 10)
# 浅拷贝
list2 = copy.copy(list1)
print('list2的内存地址为:', id(list2))
list2.append(88)
print(list1, 'list1的内存地址为:', id(list1))
print('list2的内存地址为:', id(list2))
# 结论:可变类型浅拷贝也会产生新的空间地址,保证数据独立性
print('--' * 10)
# 深拷贝
# 可变类型深拷贝会产生新的空间地址,保证数据独立性
list3 = copy.deepcopy(list1)
print('list3的内存地址为:', id(list3))
list3.append(99)
print(list1, 'list1的内存地址为:', id(list1))
print('list3的内存地址为:', id(list3))
总结:
-
浅拷贝:
- 不会产生新的空间
- 源对象和副本对象指向同一个空间
- 如果拷贝的对象,子对象不会拷贝
-
深拷贝:
- 产生新的空间
- 能够保持各自独立性
- 如果拷贝的是对象,子对象不会拷贝
简单可变类型不管深拷贝还是浅拷贝都会产生新的空间,保持各自独立性
复杂可变类型的深浅拷贝
# @date : 2022/11/2 16:40
# @author : hahazhong
# @coding : UTF-8
# @object : 熟悉复杂类型的深浅拷贝
import copy
a = [1, 2]
b = [22, 33, 44]
list = [a, b]
print('a=', a, id(a))
print('b=', b, id(b))
print('list=', list, id(list))
print('list[0]=', list[0], id(list[0]))
print('list[1]=', list[1], id(list[1<