线程基本知识
- 线程与进程比较
对比 | 线程 | 进程 |
---|---|---|
定义 | 线程是进程运行和执行的最小调度单位 | 进程是程序运行的一个实体的运行过程,是系统进行资源分配和调配的一个独立单位 |
系统开销 | 仅保存少量寄存器的内容,开销小,在进程的地址空间执行代码 | 创建撤销切换开销大,资源要重新分配和收回 |
拥有资产 | 基本上不占资源,仅有不可少的资源(程序计数器,一组寄存器和栈) | 资源拥有的基本单位 |
调度 | 独立调度分配的单位 | 资源分配的基本单位 |
安全性 | 线程共享一个进程下面的资源,可以互相通信和影响 | 进程间相互独立,互不影响 |
地址空间 | 由相关堆栈寄存器和和线程控制表TCB组成,寄存器可被用来存储线程内的局部变量 | 系统赋予的独立的内存地址空间 |
- 线程与cpu
一个CPU核心在同一时刻只能运行一个线程,但可以在一段时间内交替运行多个线程(并发);
CPU核数越多,同一时刻能够运行的线程数越多(并行);
- 操作系统实现线程目的
提高cpu利用率
Python线程
python线程被人诟病的主要原因:
由于python设计之初,没预料到多核cpu能够得到现在的发展,只考虑到了单核cpu。为了更好的实现多线程之间数据完整性与状态同步,于是设计出了一个全局解析器锁(GIL, global interpreter lock)。
GIL确保Python进程一次运行一个线程(其它线程处于等待I/O或者睡眠状态),无论当前cpu有多少核心。这就意味着Python虽然可以实现多线程,但是在任意时间点仅有一个核心在执行Python指令(即线程无法并行运算),无法发挥现代多核cpu的性能。
CPython解析只允许拥有GIL才能运行程序。理解GIL的弊端
write代表互斥锁,作用是防止多个线程一起修改某一共享数据data导致错误
1)Th1和Th2的下一步都要希望对data进行修改;
2) Th1在某一时刻同时拿到了GIL与write,准备对data进行修改;
3) Th1在准备修改前,发生了I/O请求,所以需要让出GIL(如果由于I/O请求让出GIL,该线程不参与GIL的竞争,如果由于持有时间到达上限,可参与竞争),但是write依旧才Th1手上;
4) Th2获得了GIL,但是由于缺少write也无法对data进行修改,达到持有GIL时间上限后,让出GIL,并参与竞争;
5) Th1的I/O完成后,参与GIL竞争(由于Th1持有write,所以无论Th2获得多少次GIL都无法对data进行修改,而持有GIL等待write的时间将会被浪费);
6) 直到Th1获得GIL后,由于Th1拥有GIL、write,所以对data进行修改,然后释放write,进行下一步操作,直到GIL持有时间达到上限或者进程结束,释放GIL(不持有GIL无法运行);
7) Th2获得Th1释放的write,等待GIL,获得后,修改data,直到GIL持有时间达到上限或者进程结束,释放GIL(不持有GIL无法运行);
总结
- 由于GIL的存在,所以python线程基本失去了并行计算的能力。