Golang并发解读

Stella981
• 阅读 327

进程与线程

概念

在面向进程设计的系统中,进程(process)是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。 进程是程序(指令和数据)的真正运行实例。用户下达运行程序的命令后,就会产生进程。同一程序可产生多个进程(一对多关系),以允许同时有多位用户运行同一程序,却不会相冲突。

线程(thread)是操作系统能够进行运算调度的最小单位,线程分为内核线程、轻量级进程、用户线程;内核线程是指操作系统内核调度的线程,如Win32线程;用户线程是由用户进程自行调度的线程,如Linux平台的POSIX Thread; 轻量级进程(LWP)是建立在内核之上并由内核支持的用户线程。

【内核线程】<->【轻量级进程】<->【用户线程】

Golang并发解读

在用户空间模拟操作系统对进程的调度,来调用一个进程中的线程,每个进程中都会有一个运行时系统,用来调度线程。此时当该进程获取cpu时,进程内再调度出一个线程去执行,同一时刻只有一个线程执行。

Golang并发解读

内核级线程:切换由内核控制,当线程进行切换的时候,由用户态转化为内核态。切换完毕要从内核态返回用户态。

Golang并发解读

系统调度

线程本质可以看成是一系列的指令集。

Golang并发解读

线程一般分为三种状态:

  • 阻塞态:表示线程已经停止,需要等待一些事情发生后才可继续。这有很多种原因,比如需要等待硬件(磁盘或网络),系统调用,或者互斥锁(atomic, mutexes)。这类情况导致的延迟,往往是性能不佳的根本原因。
  • 就绪态:这代表线程想要一个 CPU 核来执行被分配的机器指令。如果你有很多个线程需要 CPU,那么线程就不得不等待更长时间。此时,因为许多的线程都在争用 CPU,每个线程得到的运行时间也就缩短了。
  • 运行态:这表示线程已经被分配了一个 CPU 核,正在执行它的指令。与应用相关的工作正在被完成。这是每个人都想要的状态。

CPU密集型任务和IO密集型任务 CPU密集处理任务中线程很少进入阻塞态。它一直都需要使用 CPU,因此线程的切换并没有用,甚至会产生负面效果,主要通过多核并行来解决问题。这种工作通常都是数学计算。比如计算圆周率的第 n 位的工作就属于 CPU密集型的工作。 IO密集任务线程会经常进入阻塞态,比如网络请求资源,或者系统调用。一个需要访问数据库的线程属于 IO密集的。互斥锁的使用也属于这种。这时候线程的切换来提升并发量。

线程调度存在的问题

昂贵的代价

  • 上下文切换
  • Cache Line 命中率

上下文切换

上下文切换是指调度器把一个线程从CPU核上拿下来,把另一个就绪态的线程放到CPU核上。线程的上下文切换时间一般是50~100ns,这是为什么如果对于计算密集型的任务频繁切换反而会导致效果更差。

Cache Line 命中率

由于访问主内存很耗时间,CPU大部分会访问cache,现代CPU缓存一般分为了三层。离CPU越远的,其访问速度越慢。因此提高 Cache Line 命中率是提高性能的很重要的而一种方法,一般来说优化缓存可从三个方面入手:一、减少命中时间;二、降低失效率;三、减轻失效代价。 但是,对于多核系统来说,多线程在每个核都有一份它自己所需要数据的拷贝,随着 CPU 核上运行的线程的改变,不同的线程需要访问的数据不同,从而导致同一个 cache line 中的数据被修改了,其他所有核上的 cache line 拷贝都标记为“不可用”,当其他核上的线程试图访问或修改这个数据时,需要重新从主内存上拷贝最新的数据到自己的 cache 中。

Golang并发解读

Goroutine 的模型设计

Golang并发解读

P:是一个逻辑处理器的概念,当P有任务时需要创建或者唤醒一个系统线程来执行它队列里的任务,所以P/M绑定构成一个执行单元。 当你的 Go 程序启动之初,它会被分配一个逻辑处理器,每一个 P 会被分配一个系统线程(M)。这个 M 会被操作系统调度,操作系统把线程(M)放到一个 CPU Core 上去执行,在执行的时候,每个线程都被绑定上了一个独立的 P 。

M:是一个线程或称为Machine,所有M是有线程栈的。

G:表示一个Goroutine。

执行队列 在 Go 调度器中有 2 个不同的执行队列:全局队列(Global Run Queue, 简称 GRQ)和本地队列(Local Run Queue,简称 LRQ)。每一个 P 都会有一个 LRQ 来管理分配给 P 上的 Goroutine。这些 Goroutine 轮流被交付给 M 执行。GRQ 是用来保存还没有被分配到 P 的 Goroutine。 LRQ是 Lock-Free 的,处理速度快;GRQ为保证数据竞争问题,需要加锁处理,速度比LRQ慢,因此 P 处理完LRQ的时候会先找其他 P 的LRQ,最后再去找GRQ。

goroutine队列调度

runtime.schedule() {
    // only 1/61 of the time, check the global runnable queue for a G.
    // if not found, check the local queue.
    // if not found,
    //     try to steal from other Ps.
    //     if not, check the global runnable queue.
    //     if not found, poll network.
}

Golang并发解读 Golang并发解读

Goroutine调度为何更好?

  • Go的调度是基于用户态事件而非抢占式的,比如关键字go、垃圾回收、同步互斥操作如Lock() Unlock()
  • Go通过尽可能多在M上来运行goroutine,从而提高CPU的利用率,Go通过"spinning threads"最小化系统线程的切换。
  • 利用gorountine可以避免系统线程的切换,从而提高Cache Line 命中率。
  • 线程的stack size更大(≥ 1MB),而gorountine得stack size只有2KB,启动更慢。

更多精彩:https://www.shikanon.com/

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
4、jstack查看线程栈信息
1、介绍利用jps、top、jstack命令找到进程中耗时最大的线程,以及线程状态等等,同时最后还可以显示出死锁的线程查找:FoundoneJavaleveldeadlock即可1、jps获得进程号!(https://oscimg.oschina.net/oscnet/da00a309fa6
Stella981 Stella981
2年前
Noark入门之线程模型
0x00单线程多进程单线程与单进程多线程的目的都是想尽可能的利用CPU,减少CPU的空闲时间,特别是多核环境,今天咱不做深度解读,跳过...0x01线程池锁最早的一部分游戏服务器是采用线程池的方式来处理玩家的业务请求,以达最大限度的利用多核优势来提高处理业务能力。但线程池同时也带来了并发问题,为了解决同一玩家多个业务请求不被
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Wesley13 Wesley13
2年前
Java中多线程并发体系知识点汇总
一、多线程1、操作系统有两个容易混淆的概念,进程和线程。进程:一个计算机程序的运行实例,包含了需要执行的指令;有自己的独立地址空间,包含程序内容和数据;不同进程的地址空间是互相隔离的;进程拥有各种资源和状态信息,包括打开的文件、子进程和信号处理。线程:表示程序的执行流程,是CPU调度执行的基本单位;线程有自己的程序计数器、寄存器、堆栈和帧。同一进
Wesley13 Wesley13
2年前
Java多线程介绍
1\.线程概述1.1线程和进程进程是处于运行过程中的程序,并且具有一定的独立功能并发性:同一个时刻只能有一条指令执行,但多个进程指令被快速轮换执行并行:多条指令在多个处理器上同时执行线程是进程的执行单元1.2多
Python进阶者 Python进阶者
4个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这