12.GIL锁、 COPY

GIL锁

GIL锁引入

  • 监控资源的竞争情况 htop
  • 资源的消耗的情况
    • 单进程 1核
    • 多进程 多核
    • 多线程 多核但是很低 gil锁的问题
# 单进程、单线程死循环
def test():
    while True:
        pass

test()
# 多进程
import multiprocessing

def deadLoop():
    while True:
        pass

# 子进程死循环  进程2
p1 = multiprocessing.Process(target=deadLoop)
p1.start()

# 主进程死循环  进程1
deadLoop()
# 多线程
import threading

# 子线程死循环
def test():
    while True:
        pass

t1 = threading.Thread(target=test)
t1.start()

# 主线程死循环
while True:
    pass

GIL概念及影响

  • Gil 全局解释器锁
  • GIL跟python语言没有关系,Python底层解释器Cpython的问题
  • GIL释放的情况
    • 线程执行完毕释放
    • I/O 阻塞的时候释放
    • Python 3.x使用计时器(执行时间达到阈值后,当前线程释放GIL)

GIL解决方案

  • 解决方案:
    • 更换解释器 Jpython pypy(不推荐)
    • 用进程替换线程(进程会占用更多的系统资源)(推荐)
    • 子线程用C语言实现(test.c-->libtest.so-->python加载)(推荐)
    • 使⽤python语⾔的特性:胶⽔.
    • 我们让⼦线程部分⽤c来写,就ok。(实质上也相当于那部分代码绕过了cython解释器)
    • gcc test.c -shared -o libtest.so
      • -shared:将其编译成so⽂件
      • -o:表示output,⽤来输出的⽂件名
      • 库⽂件是以lib开头
      • 编译过后,在当前的⽂件夹下就会⽣成⼀个.so⽂件
# # test.c
# void Loop()
# {
#     while(1)
#     {
#         ;
#     }
# }

# gcc test.c -shared -o libtest.so

# 1、导入模块ctypes
import ctypes
import threading

# 2、加载so文件
mylib = ctypes.cdll.LoadLibrary("./libtest.so")

# 3、创建子线程,并且启动
t1 = threading.Thread(target=mylib.Loop)
t1.start()

# 4、主线程死循环
while True:
    pass

python中可变和不可变类型

可变不可变,是指内存中那块内容(value)是否可以被改变

  • 可变类型(mutable),创建后可以继续修改对象的内容(值)
    • 字典、列表
  • 不可变类型(unmutable) ,一旦创建就不可修改的对象(值)
    • 数字, 字符串,元组
    • 当内容发生修改,计算机重新分配一块内存空间
"""
可变: 变量创建完成后,内存内容可以在改变
不可变: 变量创建,内存空间一旦分配完成,就不能在改变了
"""

# a 变量,保存数字,不可变的
a = 5
print("a =", a, id(a))

# 修改变量值
# 重新开辟一块内存空间存放a的值
a = 5 + 1
print("a =", a, id(a))

list1 = [1, 3, 5]
print("list1 =", list1, id(list1))

# 给列表增加新的元素
list1.append(7)
print("list1 =", list1, id(list1))

# list1赋值给list2
list2 = list1
list2.append(9)
print("list2 =", list2, id(list2))
a = 5 140703238994488
a = 6 140703238994520
list1 = [1, 3, 5] 2135961382336
list1 = [1, 3, 5, 7] 2135961382336
list2 = [1, 3, 5, 7, 9] 2135961382336

简单可变类型拷贝

import copy

深拷贝与浅拷贝区别:

  • 深拷贝:copy.deepcopy(变量名)
    • 会产生新的空间
    • 能够保持各自的独立性
    • 如果拷贝的是对象,子对象也会拷贝(产生新的空间)
  • 浅拷贝:copy.copy(变量名)
    • 不会产生新的空间
    • 源对象和副本对象指向同一个空间
    • 如果拷贝的是对象,子对象不会拷贝(不会产生新的空间)

简单可变类型的拷贝总结:简单可变类型的数据不管深拷贝还是浅拷贝,都会产生新的空间,而且保持各自的独立性

import copy

list1 = [1, 3, 5, 7]
print("list1 =", list1, id(list1))
list2 = list1
print("list2 =", list2, id(list2))
list2.append(9)
print("list2 =", list2, id(list2))

# 浅拷贝
list3 = copy.copy(list1)
print("list3 =", list3, id(list3))
print("---------------------------------")
list1.append(11)
list2.append(13)
print("list1 =", list1, id(list1))
print("list2 =", list2, id(list2))
print("list3 =", list3, id(list3))

# 深拷贝
list4 = copy.deepcopy(list1)
print("list4 =", list4, id(list4))
list1.append(15)
print("---------------------------------")
print("list1 =", list1, id(list1))
print("list2 =", list2, id(list2))
print("list3 =", list3, id(list3))
print("list4 =", list4, id(list4))
list1 = [1, 3, 5, 7] 1725240142464
list2 = [1, 3, 5, 7] 1725240142464
list2 = [1, 3, 5, 7, 9] 1725240142464
list3 = [1, 3, 5, 7, 9] 1725240257152
list1 = [1, 3, 5, 7, 9, 11, 13] 1725240142464
list2 = [1, 3, 5, 7, 9, 11, 13] 1725240142464
list3 = [1, 3, 5, 7, 9] 1725240257152
list4 = [1, 3, 5, 7, 9, 11, 13] 1725239723136
---------------------------------
list1 = [1, 3, 5, 7, 9, 11, 13, 15] 1725240142464
list2 = [1, 3, 5, 7, 9, 11, 13, 15] 1725240142464
list3 = [1, 3, 5, 7, 9] 1725240257152
list4 = [1, 3, 5, 7, 9, 11, 13] 1725239723136

复杂可变类型拷贝

复杂可变类型的深浅拷贝问题

  • 浅拷贝,拷贝的是顶层对象的值,子对象不会拷贝(只是引用子对象)
  • 深拷贝,拷贝的顶层对象和对象的子对象,子对象会产生新的内存空间
import copy

a = [1, 2, 3]
b = [4, 5, 6]
c = [a, b]
print("a的地址:", id(a))
print("b的地址:", id(b))
print("c的地址:", id(c))
print("c[0]的地址:", id(c[0]))
print("c[1]的地址:", id(c[1]))
print("--" * 20)

# 浅拷贝
d = copy.copy(c)
a[0] = 10
b[0] = 40
print("d的地址:", id(d))
print("d[0]的地址:", id(d[0]), d[0])
print("d[1]的地址:", id(d[1]), d[1])
print("--" * 20)
# 深拷贝
e = copy.deepcopy(c)
a[0] = 100
b[0] = 400
print("e的地址:", id(e))
print("e[0]的地址:", id(e[0]), e[0])
print("e[1]的地址:", id(e[1]), e[1])
a的地址: 1725246712000
b的地址: 1725246708032
c的地址: 1725246711104
c[0]的地址: 1725246712000
c[1]的地址: 1725246708032
d的地址: 1725246701824
d[0]的地址: 1725246712000 [10, 2, 3]
d[1]的地址: 1725246708032 [40, 5, 6]
----------------------------------------
e的地址: 1725246640960
e[0]的地址: 1725246712128 [10, 2, 3]
e[1]的地址: 1725246716096 [40, 5, 6]

简单不可变类型拷贝

  • copy() 浅拷贝,副本和源指向同一个空间
  • deepcopy() 深拷贝,副本和源指向同一个空间
import copy

str = "helloworld"
tpl = (1, 2, 3)

print("str =", str, id(str))
print("tpl =",tpl, id(tpl))

# 浅拷贝
str2 = copy.copy(str)
tpl2 = copy.copy(tpl)
print("str2 =", str2, id(str2))
print("tpl2 =",tpl2, id(tpl2))
print("--" * 20)

# 深拷贝
str3 = copy.deepcopy(str)
tpl3 = copy.deepcopy(tpl)
print("str3 =", str3, id(str3))
print("tpl3 =",tpl3, id(tpl3))
str = helloworld 1725239643952
tpl = (1, 2, 3) 1725246702848
str2 = helloworld 1725239643952
tpl2 = (1, 2, 3) 1725246702848
str3 = helloworld 1725239643952
tpl3 = (1, 2, 3) 1725246702848

复杂不可变类型拷贝

复杂不可变类型的拷贝问题

  • 浅拷贝,直接引用
  • 深拷贝,看数据具体是可变还是不可变的,如果数据是可变的,会产生新的空间,保持数据的独立性
import copy

a = [1, 2, 3]
b = [4, 5, 6]
c = (a, b)

print("a的地址:",a, id(a))
print("b的地址:",b, id(b))
print("c的地址:",c, id(c))
print("c[0]的地址:",c[0], id(c[0]))
print("c[1]的地址:",c[1], id(c[1]))
print("--" * 20)

# 浅拷贝
d = copy.copy(c)
c[0][0] = 10
c[1][0] = 40
print("a的地址:",a, id(a))
print("b的地址:",b, id(b))
print("c的地址:",c, id(c))
print("c[0]的地址:",c[0], id(c[0]))
print("c[1]的地址:",c[1], id(c[1]))
print("d[0]的地址:",d[0], id(d[0]))
print("d[1]的地址:",d[1], id(d[1]))
print("--" * 20)

# 深拷贝
e = copy.deepcopy(c)
c[0][0] = 100
c[1][0] = 400
print("a的地址:",a, id(a))
print("b的地址:",b, id(b))
print("c的地址:",c, id(c))
print("c[0]的地址:",c[0], id(c[0]))
print("c[1]的地址:",c[1], id(c[1]))
print("e[0]的地址:",e[0], id(e[0]))
print("e[1]的地址:",e[1], id(e[1]))
a的地址: [1, 2, 3] 1725246733120
b的地址: [4, 5, 6] 1725246834112
c的地址: ([1, 2, 3], [4, 5, 6]) 1725240756544
c[0]的地址: [1, 2, 3] 1725246733120
c[1]的地址: [4, 5, 6] 1725246834112
a的地址: [10, 2, 3] 1725246733120
b的地址: [40, 5, 6] 1725246834112
c的地址: ([10, 2, 3], [40, 5, 6]) 1725240756544
c[0]的地址: [10, 2, 3] 1725246733120
c[1]的地址: [40, 5, 6] 1725246834112
d[0]的地址: [10, 2, 3] 1725246733120
d[1]的地址: [40, 5, 6] 1725246834112
----------------------------------------
a的地址: [100, 2, 3] 1725246733120
b的地址: [400, 5, 6] 1725246834112
c的地址: ([100, 2, 3], [400, 5, 6]) 1725240756544
c[0]的地址: [100, 2, 3] 1725246733120
c[1]的地址: [400, 5, 6] 1725246834112
e[0]的地址: [10, 2, 3] 1725246834048
e[1]的地址: [40, 5, 6] 1725246832704

切片拷贝、字典拷贝

  • 切片拷贝: 浅拷贝(如果是简单可变类型,底层会产生新的空间)
  • 字典拷贝: 浅拷贝

深拷贝和浅拷贝总结:

  • 拷贝简单可变的数据类型的时候 a = []
    • 深拷贝和浅拷贝是一样的 都会开辟新的空间存储数据 可以保证数据的独立性
  • 拷贝有嵌套的复杂的数据类型 a = [[],[]]
    • 浅拷贝 开辟一片空间 存放拷贝对象的地址 没办法保证数据的独立性
    • 深拷贝 开辟多片空间 存放拷贝对象的具体数据和地址 保证数据的独立性
  • 拷贝不可变的数据类型()
    • 深拷贝和浅拷贝都一样 都不会开辟新的空间 而是直接引用了被拷贝的数据的地址
  • 拷贝不可变的数据类型中嵌套了可变数据类型
    • 浅拷贝:只关心最外的数据类型是什么 如果是不可变的数据类型 直接引用 没有办法保证数据的独立性
    • 深拷贝:这个数据是否有可变的数据类型 如果有它就会开辟多个空间存储数据和地址 达到保证数据独立性的作用
  • 切片拷贝,字典拷贝
    • 在python中多数情况下都是浅拷贝
import copy

list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 简单数据类型,切片拷贝
list2 = list[:]
print("list =", list, id(list))
print("list2 =", list2, id(list2))
print("--" * 20)

# 复杂数据类型,切片拷贝
a = [1, 2, 3]
b = [4, 5, 6]
c = (a, b)

print("a的地址:",a, id(a))
print("b的地址:",b, id(b))
print("c的地址:",c, id(c))
print("c[0]的地址:",c[0], id(c[0]))
print("c[1]的地址:",c[1], id(c[1]))
print("--" * 20)
d = c[:]
print("d的地址:",d, id(d))
print("d[0]的地址:",d[0], id(d[0]))
print("d[1]的地址:",d[1], id(d[1]))
print("--" * 20)
# 字典拷贝
dict = {"age": [1, 8]}
print("dict =", dict, id(id))

dict2 = dict.copy()
dict["age"][1] = 100
print("dict =", dict, id(id))
print("dict2 =", dict2, id(id))
list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 1725247214144
list2 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 1725247210496
a的地址: [1, 2, 3] 1725240354624
b的地址: [4, 5, 6] 1725247212224
c的地址: ([1, 2, 3], [4, 5, 6]) 1725247100416
c[0]的地址: [1, 2, 3] 1725240354624
c[1]的地址: [4, 5, 6] 1725247212224
----------------------------------------
d的地址: ([1, 2, 3], [4, 5, 6]) 1725247100416
d[0]的地址: [1, 2, 3] 1725240354624
d[1]的地址: [4, 5, 6] 1725247212224
----------------------------------------
dict = {'age': [1, 8]} 1725149307392
dict = {'age': [1, 100]} 1725149307392
dict2 = {'age': [1, 100]} 1725149307392