Python高级语法
GIL(全局解释器锁)
GIL
例1:单线程死循环
while True:
pass
会占用满单核cpu
例2:双线程死循环
import threading
# 子线程死循环
def test():
while True:
pass
t1 = threading.Thread(target=test)
t1.start()
# 主线程死循环
while True
pass
双核差不多各占用50%CPU
例3:双进程死循环
import multiprocessing
def test():
while True:
pass
t1 = multiprocessing.Process(target=test)
t1.start()
while True:
pass
双核CPU都被占满
以上例子表明,真正能实现并发肯定是多进程
而多线程实际上在一个时刻真正运行的只有一个线程,其他在等待休息,而GIL就是这个现象的原因
GIL保证多线程程序同一时刻只有一个线程在执行
GIL不是Python语言自带的,而是Python的解释器(cpython)中带有的
GIL的优劣:
GIL不适用于计算密集型,即没有等待时间呢那种,且计算量、操作量极大,无法发挥出多核优势,推荐使用多进程
GIL适用于IO密集型,如网络通信、文件读写等有等待时间的程序,可以再等待过程中运行其他线程
避免GIL的方法
1.使用非cpython的解释器,如用java写的jpython
2.使用其他语言编写,例如可以用c语言编写部分程序,然后再python内调用,如下例
(1)用c语言编写死循环
#include<stdio.h>
void DeadLoop()
{
while(1)
{
;
}
}
上方执行完后的文件名未loop.c
(2)在终端将c文件编译成一个动态库的命令(Linux平台下)使其能被python调用
gcc loop.c -shared -o libdead_loop.so
(3)在python中调用上方生成的动态库
from ctypes import *
from threading import Thread
# 加载动态库
lib = cdll.LoadLibrary("./libdead_loop.so")
# 创建一个子进程,让其执行c语言编写的函数,次函数是一个死循环
t = Thread(target = lib.DeadLoop)
t.start()
# 主线程
while True:
pass
常见面试题
描述Python GIL的概念,以及它对python多线程的影响?编写一个多线程抓取网页的程序,并阐明多线程抓取程序是否可比单线程性能有提升,并解释原因。
答案:
1.Python语言和GIL并没有关系,仅仅是由于历史原因在Cpython虚拟机(解释器),难以移除GIL
2.GIL:全局解释器锁,每个线程在执行的过程都需要先获取GIL,保证同一时刻只有一个线程可以执行代码
3.线程释放GIL锁的情况:在IO操作等可能会引起阻塞的system call之前,可以暂时释放GIL,但在执行完毕后,必须重新获取GIL Python 3.x使用计时器(执行时间到达阈值后,当前线程释放GIL)或Python 2.x,tickets计数达到100
4.Python使用多进程是可以利用多核的CPU资源的
5.多线程爬取比单线程性能有提升,因为遇到IO阻塞会自动释放GIL锁
深拷贝和浅拷贝
浅拷贝
浅拷贝是对于一个对象的顶层拷贝
例1:先对对象与引用的关系有一定概念
>>a = [11, 22]
>>b = a
>>a = [1, 2]
>>b
>[11, 22]
>>b = a
>>a.append(3)
>>b
>[1, 2, 3]
例2:浅拷贝示例:
>>import copy
>>a = [11, 22]
>>b = [33, 44]
>>c = [a, b]
>>d = copy.copy(c)
>>id(c)
>2689410213320
>>id(d)
>2689410102344
>>id(c[0])
>2689410144008
>>id(d[0])
>2689410144008
上例说明浅拷贝只是对c这一层进行了拷贝(即c、d指向地址不同),内部依旧是原先的指向(即依旧是指向原先a、b的地址)
注1:copy.copy()如果是copy 元组,则会指向同一个地址,因为元组是不可变类型,意味着数据一定不能修改,copy没有意义(前提是元组内的都是指向不可变的数据)
注2:通过列表的切片也能进行copy,如b = a[:], 并且是浅拷贝
例:
import copy
>>a = [11, 22]
>>b = [33, 44]
>>c = (a, b)
>>d = copy.copy(c)
>>e = copy.deepcopy(c)
>>id(c)
>2689410213320
>>id(d)
>2689410213320
>id(e)
>2689410102344
>>a.append(88)
>>c
>([11, 22, 88], [33, 44])
>>d
>([11, 22, 88], [33, 44])
>>e
>([11, 22], [33, 44])
深拷贝
例1:
import copy
>>a = [11, 22]
>>b = a
>>id(a)
>2689406558856
>>id(b)
>2689406558856
>>c = copy.deepcopy(a)
>>id(c)
>2689410144008
>>a.append(33)
>>a
>[11, 22, 33]
>>b
>11, 22, 33]
>>c
>[11, 22]
即通过深拷贝,给c重新指向了一个新的地址,数据和原先的相同
例2:深拷贝说明
>>import copy
>>a = [11, 22]
>>b = [33, 44]
>>c