一、GIL
1、定义:
GIL全局解释器锁,每个线程执行过程中必须先获得GIL,保证同一时刻只有一个线程在执行,
GIL与python语言没关系,是CPython解释器才有的,在IO操作等可能会引起阻塞的system call之前,可以暂时释放GIL,但在执行完毕后,必须重新获取GIL Python 3.x使用计时器(执行时间达到阈值后,当前线程释放GIL)或Python 2.x,tickets计数达到100
2、GIL缺陷
1、无法利用CPU多核资源
2、执行计算密集型程序时慢,但是io密集型程序还是可以很快的,因为当某线程遇到IO操作时会释放GIL
3、如何避免GIL
1、IO密集型程序可以选多线程,计算密集型应考虑多进程(此方法缺点比较浪费资源)
2、换解释器,比如Jpython就没有GIL,(此方法缺点但是有些C模块就用不了)
3、因为python是胶水语言,可以用其他语言先编译好(比如C)
3、GIL与互斥锁的区别
GIL是解释器层面的,互斥锁是python代码层面的,为了防止两个线程同时修改一个内存数据,造成数据混乱
二、深拷贝与浅拷贝
1、先了解 “=” 操作
a = 1
b = a
在C语言中,变量b和变量a会有两个地址空间,并且a地址里的值会copy到b的地址里,但在python中,b = a 于如下代码:
int a = 1;
int *b;
b = &a;
也就是说b是指向a的,b和a指向同一块地址空间
2、浅拷贝
浅拷贝是对于一个对象的顶层拷贝,通俗的理解是:拷贝了引用,并没有拷贝内容
In [57]: a = [11,22]
In [58]: b = [33,44]
In [59]: c = [a, b]
In [60]: d = c
In [61]: e = copy.copy(c)
In [62]: id(c)
Out[62]: 139688461853896
In [63]: id(d)
Out[63]: 139688461853896
In [64]: id(e)
Out[64]: 139688443862280
In [65]: a.append(55)
In [66]: a
Out[66]: [11, 22, 55]
In [67]: c
Out[67]: [[11, 22, 55], [33, 44]]
In [68]: d
Out[68]: [[11, 22, 55], [33, 44]]
In [69]: e
Out[69]: [[11, 22, 55], [33, 44]]
In [70]: c.append(77)
In [71]: c
Out[71]: [[11, 22, 55], [33, 44], 77]
In [72]: e
Out[72]: [[11, 22, 55], [33, 44]]
In [73]:
上述代码说明:c 和 d 指向同一块地址空间,e是新创建了一块地址空间,然后copy c,但只拷贝顶层,相当于:
c = [指向a的地址(相当于C语言中的指针),指向b的地址]
e = [指向a的地址(相当于C语言中的指针),指向b的地址]
相当于a,c[0],e[0]指向同一地址空间,但是c和e指向不同的地址空间,所以当a的内容改变时c[0]和e[0]也会变,但当c.append(77),e不会变
3、深拷贝
深拷贝是对于一个对象所有层次的拷贝(递归
In [74]: a = [11,22]
In [75]: b = [33,44]
In [76]: c = [a,b]
In [77]: d = copy.deepcopy(c)
In [78]: a.append(55)
In [79]: c.append(66)
In [80]: id(c)
Out[80]: 139688461853064
In [81]:
In [81]: id(d)
Out[81]: 139688461852744
In [82]: id(a)
Out[82]: 139688461853512
In [83]: id(c[0])
Out[83]: 139688461853512
In [84]: id(d[0])
Out[84]: 139688461852936
In [85]: c
Out[85]: [[11, 22, 55], [33, 44], 66]
In [86]: d
Out[86]: [[11, 22], [33, 44]]
上述代码说明:d是新创建了一块地址空间,然后copy c,拷贝了所有曾,相当于:
c = [指向a的地址(相当于C语言的传值),b的值]
d = [a的值(相当于C语言的传值),b的值]
相当于a和c[0]与d[0]指向不同地址空间,并且c和d指向不同的地址空间,所以当a的内容改变时c[0]会变但d[0]不会变,但当c.append(66),d不会变
注意
1、无论copy还是deepcopy都会创建新的地址空间
2、如果copy.copy()和copy.deepcopy()对一个全部都是不可变的数据类型进行拷贝(如元组,且元组里也都是不可变数据类型),那么浅拷贝和深拷贝结果一样,都将不进行拷贝,因为元组是不可变数据类型,copy.copy会自动判断,如果拷贝的对象是元组他将变成指向。
但是如果拷贝的是数据有可变数据类型,即使元组是顶层(如元组中包含列表),深拷贝会进行深拷贝,但是浅拷贝依旧是指向
In [88]: a = [11,22]
In [89]: b = a
In [90]: c =copy.copy(a)
In [91]: id (a)
Out[91]: 139688461852104
In [92]: id(b)
Out[92]: 139688461852104
In [93]: id(c)
Out[93]: 139688443923272
In [94]:
In [94]: a = (11,22)
In [95]: b = a
In [96]: c = copy.copy(a)
In [97]: id(a)
Out[97]: 139688461774792
In [98]: id(b)
Out[98]: 139688461774792
In [99]: id(c)
Out[99]: 139688461774792
3、拷贝的其他方式
列表的切片,字典的copy,等号的赋值(不会创建新的地址空间),函数的形参都相当于浅拷贝