Python多线程编程(一):多线程和锁

以后但凡标题中出现数字括号的,就表示为一个短篇系列(大概3~5篇博文的样子,最多不超过10篇);对于较长的连载博文我将略去标题处的数字记号(如《汇编语言入门》系列)


进程和线程

什么是进程?什么又是线程?这个(被问烂掉的)问题的标准答案是什么并不重要。

理解二者的区别的联系才是关键:进程是一个执行中的程序(如打开任务管理器看到的系统进程),而线程的概念依托于进程,是在进程下执行的,类似于一条主线程下还能调用其他小线程;它们都是对某个任务的时间片段之描述(只是颗粒大小不同);鉴于此,我们也称进程为重量级进程,而把线程叫做轻量级进程

这里写图片描述


POSIX标准和Pthreads线程库

POSIX简介

POSIX(Portable Operating System Interface of Unix,最后一个X是为了读起来更像Unix :-)译为可移植操作系统接口;POSIX 基于但不局限于 Unix,这一标准意在期望获得源代码级的软件可移植性;换句话说,为一个POSIX兼容的操作系统编写的程序,应该可以在任何其它的POSIX操作系统(即使是来自另一个厂商)上编译执行;POSIX标准定义了操作系统应该为应用程序提供的接口:系统调用集(即程序接口或应用编程接口(Application Programming Interface,API),是应用程序同系统之间的接口)

BTW.,Linux、Unix如此相像的一个重要原因就是它们都遵循POSIX标准

Pthreads简介

在POSIX1003.1C中定义了处理线程的一系列C语言类型的API函数(定义见前文)——提供了一个可移植的多线程库,称为Pthreads库

Pthreads已然成为Linux操作系统中多线程接口的标准,并且广泛使用在大多数的Unix平台上,针对Windows操作系统,Pthreads也存在一个开源版本,称为pthread-win32


Python多线程(入门级)

Python使用兼容POSIX的线程,即上文所述的Pthreads之线程

Python提供了多种

比较简单的概念统统跳过,看下面两个栗子(后者使用了多线程)

1)不使用多线程

这里写图片描述

这里写图片描述

2)使用多线程

这里写图片描述

这里写图片描述

对比两次实验可以看出,使用多线程后程序运行总时间等于最耗时的那个线程的运行时间,即4s,而如果不用多线程则是顺序执行唯一的主线程,耗时等于4s+2s=6s

可见使用多线程,直接好处就是可以减少程序的运行时间,进而提高程序的性能


代码背后的思考:锁的概念

在上一板块,我们使用多线程让loop0、loop1实现了并发运行

注意到代码中出现了sleep(6),出现在主线程中——这一点和原来没有使用多线程的版本有些出入——这里为什么要写,如果不写休眠6秒又会怎样?想必读者怀着同样的心情,思考着这样的问题

空口无凭,让我们注释掉这句代码看看会发生什么

这里写图片描述

这里写图片描述

这里写图片描述

根据报错信息和Mac本给出的提示,问题出在:Python程序提前退出了!

分析:所谓Python程序提前退出,实际上是指Python程序的主线程提前退出——在别的线程尚未执行完之前就退出了

大雾:原来sleep(6)的作用是:保证主线程不在分支线程之前退出,我们可以为此在主线程中指定一个很大的休眠秒数,或者计算出分支线程的实际运行时间,但是这都不现实,前者对于资源无疑是一种浪费,而后者,在实际工程中,你会去想计算某个线程的运行时间嘛?!

这导致新概念的产生:

Python中的锁

为了更好的利用多核CPU,引入了多线程的概念

为了保护多线程中数据的完整性和线程间状态的同步,一个简单的办法就是加锁

这里写图片描述

这里写图片描述

全局解释器锁(GIL)

全局解释器锁(Global Interpreter Lock,GIL)是一个加在解释器上的锁,即使多个线程直接不会相互影响在同一个进程下也只有一个线程使用CPU

在同一个进程中只要有一个线程获取了全局解释器(CPU)的使用权限,那么其他的线程就必须等待该线程的全局解释器(CPU)使用权消失后才能使用全局解释器(CPU)

这里写图片描述

由于GIL(可以理解为全局排他锁)的存在,Python的多线程实际上是一种假象

当然可以选择不使用GIL,这和Python的解释器时有关的,CPython,PyPy,Psyco,都是Python的解释器,其中CPython用的比较广泛,而它是采用GIL的——这样的依赖关系在一方面导致了Python性能的低效

GIL总结起来两点,它是重量级的而且和解释器相关(也许会降低Python的性能,让Python多线程变成假的多线程,实际上的顺序单线程)

GIL只能并发(一个处理器处理多个任务),不能并行(多个处理器或者是多核的处理器同时处理多个不同的任务)(关于并行、并发的详细解释,以后将专门做一篇博文)

这里写图片描述

这里写图片描述

同步锁(Synchrolock)

同一时刻的一个进程下的一个线程只能使用一个CPU,要确保这个线程下的程序在一段时间内被CPU执行,就要用到同步锁(synchrolock)

特别的在文件IO中我们需要同步锁,比如在执行某个线程下的文件IO到一半可能会切换到另外的线程上去,这就对文件数据的完整性造成了威胁,这就需要同步锁的帮忙

使用同步锁也很简单,只需要在对公共数据的操作前后添加上锁和释放锁的操作即可

这里写图片描述

这里写图片描述

猜你喜欢

转载自blog.csdn.net/abc_12366/article/details/80231617
今日推荐