终于明白:有了线程,为什么还要有协程?

article/2025/11/6 8:37:24

并发的发展历史

其实,在早期计算机并没有包含操作系统,这个时候,这个计算机只跑一个程序,这个程序独享计算机的所有资源,这个时候不存在什么并发问题,但是对计算机的资源来说,确实是一种浪费。早期编程都是基于单进程来进行,随着计算机技术的发展,于是,操作系统出现了,操作系统改变了这种现状,让计算机可以运行多个程序,并且不同的程序占用独立的计算机资源,如内存,CPU等。

操作系统出现后:

  • 资源利用率:可以在其他程序执行过程出现等待的时候,去执行其他程序,从而提高程序的利用率
  • 公平性:所有的程序可以共享计算机资源,一种有效的方式是通过时间片的方式来让程序共享计算机资源
  • 任务通信:在编写多任务程序时,可以一个程序执行一个任务,必要时,程序之间进行通信即可

终于明白:有了线程,为什么还要有协程?

当计算机从单程序变成多程序之后,这个时候又发展出了多线程,线程是进程里面的每个执行控制流,或叫执行路线。如果没有明确的协同机制,那么每个线程将独立运行,共享着进程的内存及CPU资源,多进程多线程之间虽然让多任务并行处理的能力大大提升,但是本质上还是分时系统,并不是时间上真正的并行,解决这个问题的方式显而易见,就是让多个CPU能够同时计算任务,从而实现真正意义上的多任务并行。

并发的优势

并发可以满足多任务任务需求,比如一边写代码一边听音乐,即使编写多线程程序具有挑战性,但它仍在使用中,是因为它可以带来如下的好处:

  • 更好的资源利用
  • 在某些场景下程序的设计会更简单
  • 提升程序的响应性
  • 多进程/多任务:单个CPU下的并发,如在使用QQ的同时打开爱奇艺看剧
  • 多线程/子任务:单个应用下的并发,如博客网站可以处理不同用户的访问请求

进程的调度

单核的 CPU 一次只能执行一个任务,想要实现多任务,需要把 CPU 的运行时间切成一段一段的时间片,每个时间片运行一个程序,循环的分配时间片给不同的应用程序,由于时间片非常的短,在用户看来,就像是多个任务同时在运行。

终于明白:有了线程,为什么还要有协程?

进程的调度

在计算机的世界里,内核把CPU的执行时间切分成许多时间片,比如1秒钟可以切分为100个10毫秒的时间片,每个时间片再分发给不同的进程,通常,每个进程需要多个时间片才能完成一个请求。这样,虽然微观上,比如说就这10毫秒时间CPU只能执行一个进程,但宏观上1秒钟执行了100个时间片,于是每个时间片所属进程中的请求也得到了执行,这就实现了请求的并发执行。

终于明白:有了线程,为什么还要有协程?

流水线

CPU就好像一个流水线上的工人,不断的处理流水线上的各种信息包裹,打开包裹读取指令并执行,遇到执行慢的IO调用(或执行时间片结束)则会暂时把它放到等候区,继续处理流水线上下一个等待处理的包裹。等候区有很多这样的包裹,等待着系统的IO执行完成,当IO调用结束后,又开始进入到等待处理队列。

进程的缺点:在操作系统中,每个进程的内存空间都是独立的,这样用多进程实现并发就有两个缺点:一是内核的管理成本高,二是无法简单地通过内存同步数据,很不方便于是多线程模式就出现了。

线程的调度

线程,是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。线程总是在进程之内的。一个进程至少会包含一个线程。

如果一个进程只包含了一个线程,那么它里面的所有代码都只会被串行地执行。每个进程的第一个线程都会随着该进程的启动而被创建,它们可以被称为其所属进程的主线程。相对应的,如果一个进程中包含了多个线程,那么其中的代码就可以被并发地执行。除了进程的第一个线程之外,其他的线程都是由进程中已存在的线程创建出来的。所以,线程可以通过共享内存地址空间,解决内核的管理成本、内存同步数据的问题。

终于明白:有了线程,为什么还要有协程?

线程

由于线程运行的本质就是函数运行,函数运行时信息是保存在栈帧中的,因此 每个线程都有自己独立的、私有的栈区。

终于明白:有了线程,为什么还要有协程?

代码区

进程地址空间中的代码区,这里保存的是什么呢?从名字中有的同学可能已经猜到了,没错,这里保存的就是我们写的代码,更准确的是 编译后的可执行机器指令。

终于明白:有了线程,为什么还要有协程?

heap

heap很显然,只要知道变量的地址,也就是指针,任何一个线程都可以访问指针指向的数据,因此堆区也是线程共享的属于进程的资源。

终于明白:有了线程,为什么还要有协程?

stack

通常来说栈区是线程私有,既然有通常就有不通常的时候,不通常是因为不像进程地址空间之间的严格隔离,线程的栈区没有严格的隔离机制来保护,因此 如果一个线程能拿到来自另一个线程栈帧上的指针,那么该线程就可以改变另一个线程的栈区,也就是说这些线程可以任意修改本属于另一个线程栈区中的变量。

线程的调度与缺点:

在同一个进程中并行运行多个线程,是对在同一台计算机上并行运行多个进程的模拟。因此,线程也被称为轻量级进程。与进程调度类似,CPU在线程之间快速切换,制造了线程并行运行的假象。由于各个线程都可以访问进程地址空间的每一个内存地址,所以一个线程可以读、写,甚至清除另一个线程的堆栈。也就是说,线程之间是没有保护的。但要注意的是,每个线程都有自己的堆栈、程序计数器、寄存器等信息,这些不是共享的。

终于明白:有了线程,为什么还要有协程?

上下文切换

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

终于明白:有了线程,为什么还要有协程?

协程的调度

协程就是用户态的线程。通常创建协程时,会从进程的堆中分配一段内存作为协程的栈。线程的栈有8MB,而协程栈的大小通常只有几十 KB。而且,C库内存池也不会为协程预分配内存,它感知不到协程的存在。这样,更低的内存占用空间为高并发提供了保证,毕竟十万并发请求,就意味着10万个协程。

终于明白:有了线程,为什么还要有协程?

两级线程模型中用户线程与内核线程是一对一关系

每个协程有独立的栈,而栈既保留了变量的值,也保留了函数的调用关系、参数和返回值,CPU中的栈寄存器SP指向了当前协程的栈,而指令寄存器IP保存着下一条要执行的指令地址。因此,从协程1切换到协程2时,首先要把SP、IP寄存器的值为线程1保存下来,再从内存中找出协程2上一次切换前保存好的寄存器值,写入CPU的寄存器,这样就完成了协程切换。

终于明白:有了线程,为什么还要有协程?

在GO语言中,语言的运行时系统会帮助我们自动地创建和销毁系统级的线程。这里的系统级线程指的就是我们刚刚说过的操作系统提供的线程。而对应的用户级协程指的是架设在系统级线程之上的,由我们编写的程序完全控制的代码执行流程。用户级协程的创建、销毁、调度、状态变更以及其中的代码和数据都完全需要我们的程序自己去实现和处理。这带来了很多优势,比如,因为它们的创建和销毁并不用通过操作系统去做,所以速度会很快,又比如,由于不用等着操作系统去调度它们的运行,所以往往会很容易控制并且可以很灵活。

终于明白:有了线程,为什么还要有协程?

  • G - Goroutine,Go协程,是参与调度与执行的最小单位
  • M - Machine,指的是系统级线程
  • P - Processor,指的是逻辑处理器,P关联了的本地可运行G的队列(也称为LRQ),最多可存放256个G。

其中的M指代的就是系统级线程。而P指的是一种可以承载若干个G,且能够使这些G适时地与M进行对接,并得到真正运行的中介。

当一个正在与某个M对接并运行着的G,需要因某个事件(比如等待 I/O或锁的解除)而暂停运行的时候,调度器总会及时地发现,并把这个G与那个M分离开,以释放计算资源供那些等待运行的G使用。而当一个G需要恢复运行的时候,调度器又会尽快地为它寻找空闲的计算资源(包括M)并安排运行。另外,当M不够用时,调度器会帮我们向操作系统申请新的系统级线程,而当某个M已无用时,调度器又会负责把它及时地销毁掉。所以Go程序总是能高效地利用操作系统和计算机资源。程序中的所有goroutine也都会被充分地调度,其中的代码也都会被并发地运行,即使这样的goroutine有数以十万计,也仍然可以如此。

小 结

进程、线程、协程的区别主要体现在执行体的颗粒度上。最初的执行体任务比较简单,用一个进程就能满足需求,随着执行体做的事情越来越复杂,就出现了进程内多任务的需求。

进程和线程都属于系统级的任务,切换进程、线程都需要经历用户态跃迁内核态,切换成功后再由内核态切回用户态。

为了实现高性能,我们应该尽可能的减少异步线程。因为协程没有局部存储,相对来说空间成本就小很多,同时它又能满足需求。


http://chatgpt.dhexx.cn/article/fhF5xcIj.shtml

相关文章

Java中的多线程(线程间通信)

/学习笔记/ 线程间通信: 多个线程在处理同一资源,但是任务却不同。 先看一个例子,采用两个线程执行进行输入和输出任务: //资源class Resource{String name;String sex;}//输入class Input implements Runnable{Resource r ;// …

协程和线程的区别、协程原理与优缺点分析、在Java中使用协程

文章目录 什么是协程协程的优点与缺点协程实现原理.协程与线程在不同编程语言的实现在Java中使用协程Kilim介绍Kilim整合Java,使用举例 小总结 什么是协程 相对于协程,你可能对进程和线程更为熟悉。进程一般代表一个应用服务,在一个应用服务中可以创建多…

进程、线程和协程之间的区别和联系

文章目录 一、进程二、线程三、进程和线程的区别与联系四、一个形象的例子解释进程和线程的区别五、进程/线程之间的亲缘性六、协程 一、进程 进程,直观点说,保存在硬盘上的程序运行以后,会在内存空间里形成一个独立的内存体,这个…

简单了解线程和协程(C#)

1.为什么需要线程和协程: (1)使程序中的任务可以并发执行,让程序同时处理多个任务,提高程序的运行效率和响应速度 (2)线程和协程可以共享同一个进程的资源,避免多个进程之间的资源浪…

线程与协程

线程与协程 概念进程【进程间通信(IPC)】 线程协程 区别场景计算密集型IO密集型两种操作如何优化哪些语言对多协程的支持 概念 进程 二进制可执行文件在计算机内存里的一个运行实例。比如.exe文件是个类,进程就是new出来的那个实例&#xf…

总结:协程与线程

一、介绍 本文主要梳理下进程,线程,协程的概念、区别以及使用场景的选择。 二、进程 我们知道,一切的软件都是跑在操作系统上,真正用来干活 (计算) 的是 CPU。早期的操作系统每个程序就是一个进程,知道一个程序运行完…

Java线程协程

目录 线程的实现(OS&&JVM) 1.内核线程实现 2.用户线程实现 3.混合实现 4.Java线程的实现 ——如何实现不受Java虚拟机规范的约束 Java线程调度——系统自动完成(可能被干预) Java线程状态转换 内核线程的局限 协…

CDH6.3.2安装文档

正式安装之前,先说明一下CDH是基于Apache Hadoop及相关项目的发行版。CDH通过WEB界面管理,并提供了hadoop的两个核心功能:可扩展存储和分布式计算,以及企业级的重要功能。CDH是遵循Apache-licensed的开源软件,提供了基…

CDH数仓项目(一) —— CDH安装部署搭建详细流程

0 说明 本文以CDH搭建数据仓库,基于三台阿里云服务器从零开始搭建CDH集群,节点配置信息如下: 节点内存安装服务角色chen10216Gcloudera-scm-serverchen1038Gcloudera-scm-agentchen1048Gcloudera-scm-agent 上传需要用到的安装包&#xff…

CDH5.8安装说明

#CDH5.8安装说明 (Hadoop) 使用过Ambari,不知道是因为没有商业运作支撑还是社区活跃度有限,总体管理能力只能算凑合。很多Hadoop组件版本都不高,Spark也才1.3.X,Sqoop还是1.4.6.2.3,相对版本都比较低,而且…

大数据CDH安装详细教程

1.环境准备 1.1 服务器配置(理想配置) 1.2 修改主机名和hosts文件(所有节点) [roothadoop001 ~]# vim /etc/hosts vim /etc/hostname1.3 关闭防火墙 systemctl stop firewalld systemctl disable firewalld1.4 SSH免密登录(主节点) ssh-keygen -t rsa #分发到所有节点 ssh…

CDH6安装

官方文档 https://www.cloudera.com/documentation/enterprise/6/6.0/topics/installation.html 安装之前 JDK兼容性在不同的Cloudera Manager和CDH版本中也有所不同。某些版本的CDH 5与JDK 7和JDK 8兼容。在这种情况下,请确保所有服务都部署在同一主要版本上。例…

Cloudera(CDH) 简介和在线安装

实验背景 笔者需要维护线上的hadoop集群环境,考虑在本地搭建一套类似的hadoop集群,便于维护与管理。 Cloudera 简介 经过搜索发现Cloudera产品很适合笔者当前需求,于是开始研究Cloudera(CDH)的安装与使用,参…

CDH6.3.1安装

CDH6.3.1安装遇到很多问题,我想主要是由于条件有限,毕竟自己的电脑内存不如专业集群的内存大(如果是内存和硬盘充足,有些是可以避免的,甚至不会出现报错的情况),这里就介绍一下我用VMware安装的…

CDH安装配置

Cloudera5.14配置 准备工作 软件下载软件安装 JDK安装 所有节点 安装环境变量配置 sudo vim /etc/profile export JAVA_HOME/usr/java/default export PATH$JAVA_HOME/bin:$PATH 使用root用户 echo "JAVA_HOME/usr/java/default" >> /etc/environment …

CDH 6.3.2 安装(一)

目录 一、CDH框架介绍 1、CDH介绍 2、CDH官方网址 3、CDH官方文档 4、CDH集群扩容 5、CDH硬件要求 6、CDH k8s服务开启 二、CDH依赖安装 1、安装通用依赖 2、网络工具安装 3、防火墙服务安装 4、进程树形工具安装 5、其它依赖安装 三、Linux系统配置 1、主机名配…

CDH安装手册(自整理)

文章目录 前言1.组件版本2.集群规划(三台服务器为例)3.配置linux静态IP4.修改hosts文件,并实现免密登录5.创建集群分发脚本6.关闭防火墙和SELINUX(所有节点)7.配置NTP时钟同步8.安装jdk和mysql9.搭建本地yum源并安装10…

安装篇2 - 安装CDH

登陆Cloudera Manager平台 http://192.168.60.100:7180 账号密码:admin/admin 1.1 1.2 1.3 免费 2.1 2.2 2.3 2.4 选择CDH和Flink 2.5 将parcel包内的Hadoop,Hive等组件分发到各个节点进行解压激活 2.6 3.1 自行选择 自定义选择Zookeeper&#xff…

CDH5(CDH 5.16.1)安装

日萌社 人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度学习实战(不定时更新) CDH 6系列(CDH 6.0.0、CDH 6.1.0、CDH 6.2.0等)安装和使用 CDH5(CDH 5.16.1)安装 linux配置 1.…

CDH 6.3.2 安装(二)

目录 一、网络配置 1、静态网卡配置 2、网关配置 3、停止networkManager服务 4、重启网卡服务 二、配置静态网卡 1、修改网络配置 2、修改主机名 三、加载MySQL驱动包 1、准备文件 2、下载地址 3、加载MySQL驱动包 四、安装 cloudera-manager-daemons 五、保存当…