进程,线程,以及什么是协程
进程与线程的基础概念,我们之前都写过,这里只是来简单理一遍:
- 进程:执行中的程序,程序是存储在磁盘上的可执行文件,是静态的概念,而进程是动态的概念,进程可以看作一组有序指令+数据+资源的集合。
- 线程:我们将线程看作轻量级的进程,线程是进程内部的一条执行序列,一个进程中至少有一条线程,也就是main主函数所代表的执行序列,称之为主线程,通过可以通过线程库创建线程(函数线程)。
那么接下来看看进程和线程的区别:
- 进程是资源分配的单位,而线程是CPU调度执行的单位
- 进程是线程的集合,进程中包含数据,资源。一个进程内的所有线程都共享进程的数据,资源。
- 线程的创建代价要比进程创建代价小得多
- 线程的切换效率也要比进程的切换效率高的多
- 进程都是独立的个体,而同一个进程内的所有线程除了自己的栈区,其他资源共享进程
- 进程间通讯必须借助其他手段,而线程可以直接通过共享的空间通讯
- 同一个进程中的线程存在线程安全问题,而不同进程之间不存在
我们都知道进程和线程的区别,这也是老生常谈的问题,但是协程又是什么呢?尤其现在5G兴起,必不可免的会出现更高的并发,所以提高并发能力很重要。
协程分为两种:无栈协程和有栈协程,就像线程一样,分为用户线程,内核线程以及混合线程,当然无栈协程因为无法处理异步回调模式中上下文保存及恢复的问题,所以之后提到的协程一般都指的是有栈协程。
协程的出现:为了解决进程与线程存在效率不高的现象,所以提出了协程的概念。
那么线程与进程存在哪些问题呢?多线程还能比多进程效率高一点,所以我们从多线程进行分析:
- 内存占用问题:线程默认栈空间就不小,所以我们为了避免内存耗尽,我们尽量不要大量创建线程。
- 线程的调度问题:操作系统对线程进行调度是需要成本的,线程挂起前会保存线程上下文到栈空间,在切换到可调度线程上,这样的线程大量的存在时,线程上下文的保存切换会导致CPU的开销变很大。(虽然线程池技术可以对多线程进行优化,实现线程复用,避免频繁创建新线程和线程切换,但是在竞争共享资源时,不可避免的需要频繁的进行加锁)
- 异步回调模式:在2014年之前,C++服务器端的开发就是以异步回调模型为主流,业务流程中每一个需要等待io处理的节点都需要切断业务处理流程,保存当前上下文,设置回调函数,等io处理完成之后通过回调方法进行通知,再恢复上下文,继续业务处理流程。(每次回调都会切断栈上变量的生命周期,导致还需要使用的变量必须申请堆上保存或者存入上下文结构中)
我们知道线程可以看作轻量级进程,那么也可以把协程看作轻量级线程。
可以类似的看作这样:
接下来,我们将简单的介绍协程的前世今生:
我们在上几篇博客fork总结中知道,一个线程只需要开辟自己的线程栈,其他资源全部共享于进程,而一个线程栈的大小,我自己linux虚拟机上查询是10240K,也就是10M,当然我查询了相关博客,发现一般都是8M或者10M,这个是软限制,可以自己设置的,但是按照系统默认的10M,我们会发现3G的用户空间也就可以开辟300左右的线程,也就是说1个进程内也就开辟300来个线程,这个量放到高并发情况下,什么都不是了,所以就有了协程的产生。
所以我们这里给出协程的定义:
- 协程是一种用户级轻量线程,其执行过程类似于不带返回值的函数调用。
- 不仅拥有自己的寄存器上下文和栈空间,而且可以由用户自主调度执行
- 我们可以在一个线程里面轻松创建数十万个协程,就像是数十万次普通函数的调用一样轻松。
- 相对于普通函数只有一次进出,协程可以有多次进出的能力,它通过将函数上下文数据(主要指寄存器和函数栈)保存起来,在特定的时刻恢复回去继续执行,来实现函数的多次进出。
- 在Linux中,可以通过getcontext和swapcontext等接口来实现协程。