首页 > 其他分享 >[Golang并发]GMP模型

[Golang并发]GMP模型

时间:2024-06-23 15:43:38浏览次数:3  
标签:模型 用户 调度 Golang 并发 线程 内核 进程 GMP

什么是Goroutine

Goroutine = Golang + Coroutine。Goroutine是golang实现的协程,是用户级线程
Goroutine的特点:

  • 相比线程,其启动的代价很小,以很小栈空间启动(2Kb左右)
  • 能够动态地伸缩栈的大小,最大可以支持到Gb级别
  • 工作在用户态,切换成很小
  • 与线程关系是n:m,即可以在n个系统线程上多工调度m个Goroutine

进程、线程、Goroutine

在仅支持进程的操作系统中,进程是拥有资源和独立调度的基本单位。
在引入线程的操作系统中,线程是独立调度的基本单位,进程是资源拥有的基本单位。
在同一进程中,线程的切换不会引起进程切换。在不同进程中进行线程切换,如从一个进程内的线程切换到另一个进程中的线程时,会引起进程切换

线程创建、管理、调度等采用的方式称为线程模型。线程模型一般分为以下三种:

  • 内核级线程(Kernel Level Thread)模型
  • 用户级线程(User Level Thread)模型
  • 两级线程模型,也称混合型线程模型

三大线程模型最大差异就在于用户级线程与内核调度实体KSE(KSE,Kernel Scheduling Entity)之间的对应关系。KSE是Kernel Scheduling Entity的缩写,其是可被操作系统内核调度器调度的对象实体,是操作系统内核的最小调度单元,可以简单理解为内核级线程。

用户级线程即协程,由应用程序创建与管理,协程必须与内核级线程绑定之后才能执行。线程由 CPU 调度是抢占式的,协程由用户态调度是协作式的,一个协程让出 CPU 后,才执行下一个协程。

用户级线程(ULT)与内核级线程(KLT)比较:

特性 用户级线程 内核级线程
创建者 应用程序 内核
操作系统是否感知存在
开销成本 创建成本低,上下文切换成本低,上下文切换不需要硬件支持 创建成本高,上下文切换成本高,上下文切换需要硬件支持
如果线程阻塞 整个进程将被阻塞。即不能利用多处理来发挥并发优势 其他线程可以继续执行,进程不会阻塞

内核级线程模型

image

内核级线程模型中用户线程与内核线程是一对一关系(1 : 1)。线程的创建、销毁、切换工作都是有内核完成的。应用程序不参与线程的管理工作,只能调用内核级线程编程接口(应用程序创建一个新线程或撤销一个已有线程时,都会进行一个系统调用)。每个用户线程都会被绑定到一个内核线程。用户线程在其生命期内都会绑定到该内核线程。一旦用户线程终止,两个线程都将离开系统。

操作系统调度器管理、调度并分派这些线程。运行时库为每个用户级线程请求一个内核级线程。操作系统的内存管理和调度子系统必须要考虑到数量巨大的用户级线程。操作系统为每个线程创建上下文。进程的每个线程在资源可用时都可以被指派到处理器内核。

内核级线程模型有如下优点:
在多处理器系统中,内核能够并行执行同一进程内的多个线程
如果进程中的一个线程被阻塞,不会阻塞其他线程,是能够切换同一进程内的其他线程继续执行
当一个线程阻塞时,内核根据选择可以运行另一个进程的线程,而用户空间实现的线程中,运行时系统始终运行自己进程中的线程
缺点:
线程的创建与删除都需要CPU参与,成本大

用户级线程模型

image
用户线程模型中的用户线程与内核线程KSE是多对一关系(N : 1)。线程的创建、销毁以及线程之间的协调、同步等工作都是在用户态完成,具体来说就是由应用程序的线程库来完成。内核对这些是无感知的,内核此时的调度都是基于进程的。线程的并发处理从宏观来看,任意时刻每个进程只能够有一个线程在运行,且只有一个处理器内核会被分配给该进程。

从上图中可以看出来:库调度器从进程的多个线程中选择一个线程,然后该线程和该进程允许的一个内核线程关联起来。内核线程将被操作系统调度器指派到处理器内核。用户级线程是一种”多对一”的线程映射

用户级线程有如下优点:

创建和销毁线程、线程切换代价等线程管理的代价比内核线程少得多, 因为保存线程状态的过程和调用程序都只是本地过程
线程能够利用的表空间和堆栈空间比内核级线程多
缺点:

线程发生I/O或页面故障引起的阻塞时,如果调用阻塞系统调用则内核由于不知道有多线程的存在,而会阻塞整个进程从而阻塞所有线程, 因此同一进程中只能同时有一个线程在运行
资源调度按照进程进行,多个处理机下,同一个进程中的线程只能在同一个处理机下分时复用

两级线程模型

image

两级线程模型中用户线程与内核线程是一对一关系(N : M)。两级线程模型充分吸收上面两种模型的优点,尽量规避缺点。其线程创建在用户空间中完成,线程的调度和同步也在应用程序中进行。一个应用程序中的多个用户级线程被绑定到一些(小于或等于用户级线程的数目)内核级线程上。

Golang的线程模型

Golang在底层实现了混合型线程模型。M即系统线程,由系统调用产生,一个M关联一个KSE,即两级线程模型中的系统线程。G为Groutine,即两级线程模型的的应用及线程。M与G的关系是N:M。

image

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

GMP调度流程如下:
线程M想运行任务就需得获取 P,即与P关联。
然从 P 的本地队列(LRQ)获取 G
若LRQ中没有可运行的G,M 会尝试从全局队列(GRQ)拿一批G放到P的本地队列,
若全局队列也未找到可运行的G时候,M会随机从其他 P 的本地队列偷一半放到自己 P 的本地队列。
拿到可运行的G之后,M 运行 G,G 执行之后,M 会从 P 获取下一个 G,不断重复下去。

G-M-P的数量

G 的数量:

理论上没有数量上限限制的。查看当前G的数量可以使用runtime. NumGoroutine()

P 的数量:

由启动时环境变量 $GOMAXPROCS 或者是由runtime.GOMAXPROCS() 决定。这意味着在程序执行的任意时刻都只有 $GOMAXPROCS 个 goroutine 在同时运行。

M 的数量:

go 语言本身的限制:go 程序启动时,会设置 M 的最大数量,默认 10000. 但是内核很难支持这么多的线程数,所以这个限制可以忽略。 runtime/debug 中的 SetMaxThreads 函数,设置 M 的最大数量 一个 M 阻塞了,会创建新的 M。M 与 P 的数量没有绝对关系,一个 M 阻塞,P 就会去创建或者切换另一个 M,所以,即使 P 的默认数量是 1,也有可能会创建很多个 M 出来。

标签:模型,用户,调度,Golang,并发,线程,内核,进程,GMP
From: https://www.cnblogs.com/DCFV/p/18263505

相关文章

  • 掌握Perl并发:线程与进程编程全攻略
    掌握Perl并发:线程与进程编程全攻略引言Perl作为一种功能强大的编程语言,提供了丰富的并发编程手段。无论是通过threads模块实现的线程,还是通过fork系统调用产生的进程,Perl都能帮助开发者高效地处理多任务。本文将深入探讨如何在Perl中使用线程和进程,带领读者掌握并发编程的......
  • 数据库系统概论(超详解!!!) 第十四节 数据库并发控制机制
    多用户数据库系统:允许多个用户同时使用的数据库系统例:飞机定票数据库系统银行数据库系统特点:在同一时刻并发运行的事务数可达数百上千个多事务执行方式:(1)事务串行执行每个时刻只有一个事务运行,其他事务必须等到这个事务结束以后方能运行。不能充分利用系统资源,发挥数据库......
  • [Golang基础]Goroutine
    协程(CoRoutine)是一种轻量级的用户态线程。简单来说,线程(thread)的调度是由操作系统负责,线程的睡眠、等待、唤醒的时机是由操作系统控制,开发者无法决定。使用协程,开发者可以自行控制程序切换的时机,可以在一个函数执行到一半的时候中断执行,让出CPU,在需要的时候再回到中断点继续执行。......
  • 面试八股之线程篇2.5——线程中的并发锁篇——AQS
    ......
  • [Golang并发]Sync.Mutex
    源码//Copyright2009TheGoAuthors.Allrightsreserved.//UseofthissourcecodeisgovernedbyaBSD-style//licensethatcanbefoundintheLICENSEfile.//Packagesyncprovidesbasicsynchronizationprimitivessuchasmutual//exclusionlocks.......
  • [Golang并发]Sync.map
    sync.Map的实现原理可概括为:通过read和dirty两个字段将读写分离,读取时会先查询read,不存在再查询dirty,写入时则只写入dirty,所以read相当于dirty的缓存。读取read并不需要加锁,而读或写dirty都需要加锁。misses字段统计read被穿透的次数,被穿透指需要读dirty的情......
  • 粘包问题、socketserver模块实现并发
    TCP协议------------黏包现象11.服务端连续执行三次recv22.客户端连续执行三次send3问题:服务端一次性接收到了客户端三次的消息该现象称为"黏包现象"4--------------------------------------5黏包现象产生的原因:61.收消息的时候,不知道每次接收的数据到......
  • golang如何使用指针灵活操作内存?unsafe包原理解析
    Hi你好,我是k哥。一个大厂工作6年,还在继续搬砖的后端程序员。我们都知道,C/C++提供了强大的万能指针void*,任何类型的指针都可以和万能指针相互转换。并且指针还可以进行加减等算数操作。那么在Golang中,是否有类似的功能呢?答案是有的,这就是我们今天要探讨的unsafe包。本文将深入探......
  • golang runtime.Caller 获取调用堆栈信息, Caller(1) 和 Caller(2) 的区别
     funcwhoCalledMe(){//获取调用堆栈信息_,fileName,lineNo,ok:=runtime.Caller(2)if!ok{fmt.Println("Failedtogetcallerinformation")return}fmt.Printf("Calledfrom:%s:%d\n",fileName,lineNo......
  • ConcurrentHashMap(并发工具类)
    并发工具类在JDK的并发包里提供了几个非常有用的并发容器和并发工具类。供我们在多线程开发中进行使用。5.1ConcurrentHashMap5.1.1概述以及基本使用在集合类中HashMap是比较常用的集合对象,但是HashMap是线程不安全的(多线程环境下可能会存在问题)。为了保证数据的安全性我......