进程、线程和协程有什么区别(线程与协程的区别)

网友投稿 3406 2022-09-03


进程、线程和协程有什么区别(线程与协程的区别)

什么是进程和线程

有一定基础的小伙伴们肯定都知道进程和线程。

进程是什么呢?

直白地讲,进程就是应用程序的启动实例。比如我们运行一个游戏,打开一个软件,就是开启了一个进程。

进程拥有代码和打开的文件资源、数据资源、独立的内存空间。

有人会认为,要提升CPU的利用率,可以开多个进程,但是开多个进程的话,进程间通讯是个比较麻烦的事情(进程之间地址空间是独立的,需要通过其他方式,例如:管道来解决)

相反,线程之间是可以实现数据共享的,因为线程之间使用的是同一个地址空间。

线程又是什么呢?

线程又叫做轻量级进程,是CPU调度的最小单位,线程从属于进程,是程序的实际执行者。

一个进程至少包含一个主线程,也可以有更多的子线程。

多个线程共享所属进程的资源,同时线程也拥有自己的专属资源、拥有自己的栈空间

线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据

线程和进程一样都是由操作系统的调度器来统一调度,线程本身的数据结构需要占用内存,频繁创建和销毁线程会加大系统的压力。另外,线程太多,系统调度的开销也会增大。

基于上面的情况,提出了线程池解决方案,在初始化的时候批量创建线程,然后用户后续通过队列等方式提交业务逻辑,线程池中的线程进行逻辑的消费工作,这样就可以在操作的过程中降低线程创建和销毁的开销,但是调度的开销还是存在的。

有人给出了很好的归纳:

对操作系统来说,线程是最小的执行单元,进程是最小的资源管理单元。

在多核场景下,如果是I/O密集型场景,就算开多个线程来处理,也未必能提升CPU的利用率,反而会增加线程切换的开销。另外,多线程之间假如存在临界区或者共享数据,那么同步的开销也是不可忽视的。

协程,正好可以解决上面的相关问题。

这个时候我们的主角 协程 就要登场了。

协程,英文Coroutines,是一种比线程更加轻量级的存在。正如一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程。协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。协程与线程主要区别是它将不再被内核调度,而是交给了程序自己而线程是将自己交给内核调度,所以也不难理解golang中调度器的存在。

协程

定义:协程是轻量级线程。

在一个用户线程上可以跑多个协程,这样就提高了单核的利用率。协程不像进程或者线程,可以让系统负责相关的调度工作,协程是处于一个线程中,系统是无感知的,所以需要在该线程中阻塞某个协程的话,就需要手工进行调度。

协程是一种用户态的轻量级线程,协程的调度完全由用户控制。一个线程可以拥有多个协程,协程不是被操作系统内核所管理,而完全是由程序所控制。与其让操作系统调度,不如我自己来,这就是协程

要在用户线程上实现协程是一件很难受的事情,原理类似于调度器根据条件的改变不停地调用各个协程的callback机制,但是前提是大家都在一个用户线程下。要注意,一旦有一个协程阻塞,其他协程也都不能运行了。因此要处理好协程。

最重要的是,协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)。

这样带来的好处就是性能得到了很大的提升,不会像线程切换那样消耗资源。

这段代码十分简单,即使没用过python的小伙伴应该也能基本看懂。

代码中创建了一个叫做consumer的协程,并且在主线程中生产数据,协程中消费数据。

其中yield 是python当中的语法。当协程执行到yield关键字时,会暂停在那一行,等到主线程调用send方法发送了数据,协程才会接到数据继续执行。

但是,yield让协程暂停,和线程的阻塞是有本质区别的。协程的暂停完全由程序控制,线程的阻塞状态是由操作系统内核来进行切换。

因此,协程的开销远远小于线程的开销。

协程的应用

有哪些编程语言应用到了协程呢?我们举几个栗子:

Lua语言

Lua从5.0版本开始使用协程,通过扩展库coroutine来实现。

Python语言

正如刚才所写的代码示例,python可以通过 yield/send 的方式实现协程。在python 3.5以后, async/await 成为了更好的替代方案。

Go语言

Go语言对协程的实现非常强大而简洁,可以轻松创建成百上千个协程并发执行。

Java语言

如上文所说,Java语言并没有对协程的原生支持,但是某些开源框架模拟出了协程的功能,有兴趣的小伙伴可以看一看Kilim框架的源码:

总结:

多进程的出现是为了提升CPU的利用率,特别是I/O密集型运算,不管是多核还是单核,开多个进程必然能有效提升CPU的利用率。而多线程则可以共享同一进程地址空间上的资源,为了降低线程创建和销毁的开销,又出现了线程池的概念,最后,为了提升用户线程的最大利用效率,又提出了协程的概念。

通信方式:

线程的生命周期

创建:线程从创建到被cpu执行之前的这个阶段。

就绪:指线程已具备各种执行条件,一旦获取cpu便可执行。

运行:表示线程正获得cpu在运行。

阻塞:指线程在执行中因某件事而受阻,处于暂停执行的状态,阻塞的线程不会去竞争cpu。

终止:线程执行完毕,接下来会释放线程占用的资源。

进程调度算法


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Eolink DevOps 私有云 8.6 版本更新:API 管理支持一键导入 Jmeter 文件,强化系统安全等
下一篇:Java Spring框架的概述
相关文章

 发表评论

暂时没有评论,来抢沙发吧~