目录
并发编程中涉及的概念:
同步:同步的概念并非字面意思所讲,是指同步协调相互配合的意思,同步就是:在一个功能发出调用,没有得到返回将会一直等待,直到处理完毕返回结果,才会执行下一个调用。例如:去麦当劳吃肯德基,点餐过后,一直在前台等候,等待后厨做好,然后离开。
异步:与同步相反的便是异步,异步就是当一个功能发出调用,无需等待任何返回,可直接执行下个调用,当上一个调用有返回结果后,通知去取结果即可。例如:你在麦当劳点餐后,回到餐位开始做别的事情,当餐做好后通知你去取餐。
并发:并发更多的是强调的是一起出发,两个或多个程序在一个cup上执行,相互穿插运行,这就是并发。例如:两个人交替使用一台咖啡机。可以理解为支持交替做事的能力就是支持并发
并行:并行更多的是强调的一起执行,两个或多个cpu上的程序一起运行,单个cpu为并发,多个并行执行的程序。例如:两个人,两台咖啡机一人使用一个。可以理解为同时做不同事情的能力就是支持并行。
顺序执行:当你在某件事,一直等这件事情做完你才开始做其他事情,这就是顺序执行。这一点和同步非常相似,但是同步,当在等餐是是处于一个挂起的状态,结合程序更容易理解。
使程序变快的三种方式:进程,线程与协程。
进程概念:
- 进程是资源分配的最小单位。
- 一个程序只有被系统分配了内存的等资源,才能运行,一个运行起来的程序就是一个进程。
- 进程是为了在cpu上实现多道编程而提出的。多道编程:在计算机中同时存放几道相互独立的程序,他们共享系统资源,相互穿插运行。
- 一个程序至少有一个进程,一个进程至少有一个线程。
- 在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下实现并发的执行。
线程的概念:
- 线程是操作系统调度最小的单位。
- 被包含在进程之中,是进程中的实际运作单位。
- 进程无法自己执行,要操作cpu,必须要创建一个线程。
- 无论你开启多少个线程你有多少个cpu,python在执行的时候,在同一时刻只允许一个线程运行。
- 线程之间共享内存空间。
- 线程之间执行是无序的,它是由cpu调度决定的,cpu调度那个线程哪个就执行。
协成的概念:
- 协程又叫微线程,协程是是一种用户态的轻量级线程。
- 线程遇到I/O操作会等待,阻塞,协成遇到I/O会自动切换,最后只留CPU运算
- 线程的状态保存在cpu的寄存器和栈里,而协成拥有自己的空间,所以无需上下文切换的开销,所以快。
- 协成能保留上次调用时的状态,每当过程重入时都相当于进入上一次的调用状态,
以上是三程之间的区别与基本概念,目前没有理解没有关系在下面会一一讲解:我们为什么要使用并发编程,三程之间如何使用,彼此区别与关系,以及实战应用。
为什么要引入并发编程:
场景一:一个网络爬虫普通爬取需要一个小时,当你采用多线程并发技术,这时只需要20分钟。、
场景二:当一个app页面打开需要三秒,而当你将后台不重要程序进行异步化,就可以极大的提高页面打开的速度。
并发技术是书写程序绕不开的话题,尤其是对于python而言,是提高python运行速度的最简途径之一。
常见的程序运行方式:
单线程运行:也就是不加改造的程序,程序在cpu上运行如果遇到I/O操作(将硬盘数据从内核态拷贝到用户态的过程,比如读取文件)将会阻塞程序运行,当程序读取完毕才会继续像下执行。所以是非常耗费时间的。
多线程并发:当程序遇到I/O操作则切换到其它线程,自己则继续执行,实现异步操作,CPU和I/O是可以同时运行的,这点需要注意。
多CPU并行:其实就是多进程并行,上面提到进程可以将CPU多核资源利用,多个CPU同时运行。
多机器并行:搭建集群等,这里不做过多说明,有兴趣可以了解。
在上面我们提到了并发和并行的概念,在这里也一目了然。
python 对并发编程的支持有哪些?
多线程:threading,利用CPU和I/O同时执行的原理,让I/O可以异步操作。
多进程:multiprocessing,利用CPU多核资源,真正的实现了并行执行。
异步I/O:asyncio, 在单线程模式下利用CPU和I/O同时执行的原理,实现函数异步任务。
lock线程锁,防止多线程访问同一资源出现冲突。
使用Queue实现不同进程/线程之间的通信实现生产者-消费者模式,比如爬虫边爬取边解析。
线程池pool和进程pool,简化线程/进程任务提交,等待结束,获取结果。
使用subprocess启动外部程序进程,并进行输入和输出交互。
我们如何去选择多进程,多线程,多协程?
首先我们要了解两个概念:I/O密集型与CPU密集型。
I/O密集型:I/O密集型是指系统运作大部分的状况是cpu在等待I/O的读写操作,cpu占用率仍然较低。例如:文件处理,网络爬虫爬取,读写数据库程序。
CPU密集型:又叫做计算密集型,是指I/O在很短时间内就可以完成,CPU需要大量的计算和处理,特点是cpu占用率相当高。比如:压缩解压缩,加密解密,正则表达式搜索等需要大量计算的。
多线程(thread):
优点:相比于进程,更轻量级,占用资源少(线程的需要内存去保存上下文变量)。
缺点:相比于进程:多线程只能并发的执行,不能利用多核cpu,因为GIL锁(稍后会说到)的原因。相比于协成:启动数量有限制,占用内存资源,有线程切换开销
多进程(process):
一个进程可启用多个线程。
优点:可以利用多核资源
缺点:占用资源过多,可启动数目比线程少。
适用于:cpu密集型
多协程(coroutine):
一个线程中可以启动超多数量协程
优点:内存开销做少,启动协成数量最多
缺点:支持的库有限制,代码实现复杂,对于有些库不支持比如requests,但给出aiohttp。
适用于:io密集型计算,需要超多任务运行,但有必须有现成库支持的场景。
多进程与多线程区别:
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
简单点来说:
- 一个程序至少有一个进程,一个进程至少有一个线程。
- 线程的划分尺度要小于进程,使得多线程并发性高,创建线程的代价要小于进程。
- 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
- 但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
- 系统并未将多线程看做独立应用,而进程是系统分配资源的基本单位,这就是线程和进程的重要区别。
对于多线程和多进程的区别比较明确,对于多协程与多线程如何去使用,到底该使用哪个,主要在于判断协程实现起来是否特别复杂,有无三方库支持,如果支持较好,能使用协程就使用协程。