kotlin之协程(一),线程,进程,协程,协程可以替换线程吗?

目录

kotlin之协程(一),线程,进程,协程,协程可以替换线程吗?
kotlin之协程(二),Kotlin协程是什么、挂起是什么、挂起的非阻塞式
kotlin之协程(三),开始创建协程,launch,withContext
kotlin之协程(四),协程的核心关键字suspend
kotlin之协程(五),launch 函数以及协程的取消与超时
kotlin之协程(六),协程中的 async和launch的区别以及runBlocking
kotlin之协程(七),协程中relay、yield 区别

前言

在DataStore里面有提到 DataStore是基于协程Flow实现的,那么什么是协程呢?

先来回顾一下线程和进程

在了解协程之前,我们先回归一下进程和线程的概念

  • 进程 直白地讲,进程就是应用程序的启动实例
  • 线程 线程从属于进程,是程序的实际执行者。一个进程至少包含一个主线程,也可以有更多的子线程。并且线程拥有自己的栈空间
线程进程.jpeg

总结起来就是:对操作系统来说,线程是最小的执行单元,进程是最小的资源管理单元

无论进程还是线程都是有操作系统统一管理的
而线程具有五种状态,我们看一下状态的转化关系


线程转化关系.png

接下来我们了解一下并发和并行

  • 并发
    并发意味着应用程序同时(同时)在一项以上的任务上取得进展。好吧,如果计算机只有一个CPU,则应用程序可能无法 在同一时间同时完成一项以上的任务,而是在应用程序中一次要处理多个任务。在开始下一项任务之前,它并没有完全完成一项任务。而是,CPU在不同任务之间切换,直到任务完成为止。


    并发.png

即使其中只有一个线程在运行,也可以有一个并发应用程序

  • 并行
    并行是指应用程序将其任务分解为较小的子任务,这些子任务可以并行处理,例如在多个CPU上同时进行。
并行.png

为了实现真正的并行性,您的应用程序必须运行多个线程,或者至少能够调度要在其他线程,进程,CPU,图形卡等中执行的任务。

不得不说的进程和线程的痛点

(1)线程的调度与缺点

在同一个进程中并行运行多个线程,是对在同一台计算机上并行运行多个进程的模拟。因此,线程也被称为轻量级进程。与进程调度类似,CPU在线程之间快速切换,制造了线程并行运行的假象。

由于各个线程都可以访问进程地址空间的每一个内存地址,所以一个线程可以读、写,甚至清除另一个线程的堆栈。也就是说,线程之间是没有保护的。但要注意的是,每个线程都有自己的堆栈、程序计数器、寄存器等信息,这些不是共享的。

共享地址空间虽然可以方便地共享对象,但这也导致一个问题,那就是任何一个线程出错时,进程中的所有线程会跟着一起崩溃。这也是如Nginx等强调稳定性的服务坚持使用多进程模式的原因。事实上,无论基于多进程还是多线程,都难以实现高并发,这由三个原因所致

(2)线程内存占用

一是单个线程消耗的内存过多,比如64位的Linux为每个线程的栈分配了8MB的内存,通过ulimit -s可以查看线程的默认分配的内存。单位kb

(3)线程竞争

为了解决线程申请堆内存时,互相竞争的问题。每个线程预先在这个空间内申请堆空间还预分配了64MB的内存作为堆内存池。所以,我们没有足够的内存去开启几万个线程实现并发。

(4)线程切换耗时

线程的切换是由内核控制的,什么时候会切换线程呢?不只时间片用尽,当调用阻塞方法时,内核为了CPU 充分工作,也会切换到其他线程执行。一次上下文切换的成本在几十纳秒到几微秒间,当线程繁忙且数量众多时,这些切换会消耗绝大部分的CPU运算能力。

下图以磁盘IO为例,描述了多线程中使用阻塞方法读磁盘,2个线程间的切换方式。

线程切换耗时.png

那么,怎么才能实现高并发呢?把上图中本来由内核实现的请求切换工作,交由用户态的代码来完成就可以了。

协程

什么是协程

协程 - 也叫微线程,是一种新的多任务并发的操作手段

这中说话不够详细,我们换种说话

协程就是用户态的线程。通常创建协程时,会从进程的堆中分配一段内存作为协程的栈。线程的栈有8MB,而协程栈的大小通常只有几十 KB,更低的内存占用空间为高并发提供了保证

  • 下面是关于协程这个概念的一些描述:

协程的开发人员 Roman Elizarov 是这样描述协程的:协程就像非常轻量级的线程。线程是由系统调度> 的,线程切换或线程阻塞的开销都比较大。而协程依赖于线程,但是协程挂起时不需要阻塞线程,几乎>是无代价的,协程是由开发者控制的。所以协程也像用户态的线程,非常轻量级,一个线程中可以创建>任意个协程。

对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。协程能保留上一次调用时的状态,不需要像线程一样用回调函数,所以性能上会有提升。缺点是本质是个单线程,不能利用到单个CPU的多个核

协程的调度

语言的运行时系统会帮助我们自动地创建和销毁系统级的线程。这里的系统级线程指的就是我们刚刚说过的操作系统提供的线程。

PHP协程的问题

为了保证所有切换都在用户态进行,协程必须重新封装所有的阻塞系统调用,否则,一旦协程触发了线程切换,会导致这个线程进入休眠状态,进而其上的所有协程都得不到执行。所以,协程的高性能,建立在切换必须由用户态代码完成之上,这要求协程生态是完整的,要尽量覆盖常见的组件

为什么要搞出和用协程呢

  • 是节省CPU,避免系统内核级的线程频繁切换,造成的CPU资源浪费。好钢用在刀刃上。而协程是用户态的线程,用户可以自行控制协程的创建于销毁,极大程度避免了系统级线程上下文切换造成的资源浪费。

  • 是节约内存,在64位的Linux中,一个线程需要分配8MB栈内存和64MB堆内存,系统内存的制约导致我们无法开启更多线程实现高并发。而在协程编程模式下,可以轻松有十几万协程,这是线程无法比拟的。

  • 是稳定性,前面提到线程之间通过内存来共享数据,这也导致了一个问题,任何一个线程出错时,进程中的所有线程都会跟着一起崩溃。

  • 是开发效率,使用协程在开发程序之中,可以很方便的将一些耗时的IO操作异步化,例如写文件、耗时IO请求等。

对于协程的一个总结

  • 特征:协程是运行在单线程中的并发程序
  • 优点:省去了传统 Thread 多线程并发机制中切换线程时带来的线程上下文切换、线程状态切换、Thread 初始化上的性能损耗,能大幅度唐提高并发性能
  • 简单理解:在单线程上由程序员自己调度运行的并行计算

写到最后

协程本身不是替换线程的,因为协程是建立在线程之上的,但是协程能够更好的为我们提供执行高并发任务
但是kotlin的协程的概念是否真的是这样的呢?

(每天学习一点点.每天进步一点点,分享不宜路过点个赞呀,喜欢的点个关注后续更新不断).

你可能感兴趣的:(kotlin之协程(一),线程,进程,协程,协程可以替换线程吗?)