Go语言中的并发goroutine底层原理

目录
  • 一、基本概念
    • ①并发、并行区分
    • ②从用户态线程,内核态线程阐述go与java并发的优劣
    • ②高并发为什么是Go语言强项?
    • ③Go语言实现高并发底层GMP模型原理解析
  • 二、上代码学会Go语言并发
    • ①.开启一个简单的线程
    • ②.动态的关闭线程

一、基本概念

①并发、并行区分

1.概念

  • 并发:同一时间段内一个对象执行多个任务,充分利用时间
  • 并行:同一时刻,多个对象执行多个任务

2.图解

类似于超市柜台结账,并行是多个柜台结多个队列,在计算机中是多核cpu处理多个go语言开启的线程,并发是一个柜台结账多个队列,在计算机中就是单核cpu处理多个任务,抢夺时间片.

②从用户态线程,内核态线程阐述go与java并发的优劣

1.用户态线程、内核态线程差异

  • 用户态:只能受限制的访问内存,且不允许访问外围设备,占用CPU资源可以被其他程序抢走。
  • 内核态:CPU可以访问内存所有数据,包括外围设备,例如硬盘网卡等,cpu可以将自己从一个程序切换到另一个程序

2.java与go并发差异:

java:

  • java没有规定具体使用什么线程,而是在不同形态的线程上进行切换,会耗费相当的资源
  • go是用户态线程,资源耗费较少,一个线程的栈体默认为1M,并且需要运行在JVM上

go:

  • go语言并发通过,goroutine实现,属于用户态的线程,可以根据需要创建成千上万个goroutine,每个goroutine占用内存大小会根据需要动态生成,典型的大小为2kB可以按需求放大到1GB,在go语言中一次可以轻松创建十万左右的goroutine,并且不依赖运行环境。

②高并发为什么是Go语言强项?

1.历史背景

Go语言产生较晚,在其产生之前就已经有了多核cpu,所以设计者的理念就是将这门新的语言使用到多核cpu上支持更大数量级的并发

2.自身原因

Go语言多并发底层实现使用的是协程,他占有更少的资源具有更快的执行速度,占用的资源还会根据 任务量进行扩大或者缩小

③Go语言实现高并发底层GMP模型原理解析

1. G:
G是Goroutine的缩写,在这里就是Goroutine的控制结构,是对Goroutine的抽象。其中包括执行的函数指令及参数;G保存的任务对象;线程上下文切换,现场保护和现场恢复需要的寄存器(SP、IP)等信息。在 Go 语言中使用 runtime.g 结构表示。

2. M:

表示操作系统线程也可以称为内核线程,由操作系统调度以及管理,调度器最多可以创建 10000 个线程,在 Go 语言中使用 runtime.m 结构表示。(用户线程与内核线程的映射关系)

3. P:

调度各个goroutine,使他们之间协调运行逻辑处理器,但不代表真正的CPU的数量,真正决定并发程度的是P,初始化的时候一般会去读取GOMAXPROCS对应的值,如果没有显示设置,则会读取默认值,在Go1.5之后GOMAXPROCS被默认设置可用的核数,而之前则默认为1,在 Go 语言中使用 runtime.p 结构表示。

4.指定cpu线程个数

通过runtime.GOMAXPROCS(),可以指定P的个数,如果没有指定则默认跑满整个cpu

二、上代码学会Go语言并发

①.开启一个简单的线程

开启线程使用go+函数,以下案例要认识到开启多线程使用函数闭包可能会出现的问题

1.使用匿名函数开启线程

//打印1-1000
    for i := 0; i < 1000; i++ {
        go func() {
            fmt.Println(i)
        }()
    }
//这里使用了函数闭包
/*
打印结果
    995
    995
    995
    996
    996
    999
    1000
    1000
*/

2.出问题的原理:

匿名函数进行操作时会将当前环境内的变量进行闭包,由于启动线程需要一定时间在启动线程的时候i进行了改变所以打印的时候会有许多值相同

②.动态的关闭线程

1.为什么需要进行动态的关闭线程?

在Go语言中如果不进行动态的关闭线程,那么有可能在子线程没有执行结束主线程就结束了,那样的话会有 程序安全隐患,所以主线程不可以直接结束,应作为后盾,直到所有线程都结束了才可以结束。

2.使用waitGroup

 waitGroup有三个方法常用:

  • waitGroup.Add():使用wait计数器记1次数//将创建的线程数传进去
  •  waitGroup.Done():wait计数器减1(放在被开启线程的函数内)
  • waitGroup.Wait():阻塞等待wait计数器值为零(放在主线程内)

defer是在函数主体执行完的时候执行的代码(可理解为延时执行)

代码如下:

package main

import (
    "fmt"
    "math/rand"
    "sync"
    "time"
)
// 定义一个waitGroup结构体变量
var wg sync.WaitGroup

func f(i int) {
    // 等到函数执行完毕,会将waitGroup内的计数器减一
    defer wg.Done()
    rand.Seed(time.Now().UnixNano())
    time.Sleep(time.Duration(rand.Intn(3)) * time.Second)
    fmt.Println(i)
}
func main() {
    for i := 0; i < 100; i++ {
        // 开启一个线程就使用waitGroup记一次数
        wg.Add(1)
        go f(i)
    }
    // 阻塞等待waitGroup计数器为0
    wg.Wait()
    fmt.Println("hello")
}

总结:
Go语言的高并发奠定了其未来在高级语言中的地位,越来越多的用户加入互联网需要一个支持高并发语言的支持,亿万级电商秒杀,亿万级游戏用户同时在线都离不开这样的语言,所以Go语言一直被游戏后端、web后端项目开发者青睐。

到此这篇关于Go语言中的并发goroutine底层原理的文章就介绍到这了,更多相关Go语言中的并发goroutine内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Go并发的方法之goroutine模型与调度策略

    目录 单进程操作系统 多线程/多进程操作系统 1:N模型 M:N模型 goroutine goroutine早期调度器 GMP 调度器设计策略 复用线程 并行 抢占 全局队列 学习刘丹冰<8小时转职golang工程师>,本节都是原理 单进程操作系统 早期的单进程操作系统,可以理解为只有一个时间轴,CPU顺序执行每一个进程/线程,这种顺序执行的方式,CPU同一时间智能处理一个指令,一个任务一个任务去处理 这样就会导致进程阻塞的话,CPU就会卡在当前进程,一直在等待,CPU就会浪费 多线程/多进程

  • 分析Go语言中CSP并发模型与Goroutine的基本使用

    目录 一.并发实现模型 1.1.多进程 1.2.多线程 1.3.协程 二.共享内存与CSP 三.Goroutine 一.并发实现模型 1.1.多进程 在之前的文章当中我们曾经介绍过,进程是操作系统资源分配的最小单元.所以多进程是在操作系统层面的并发模型,因为所有的进程都是有操作系统的内核管理的.所以每个进程之间是独立的,每一个进程都会有自己单独的内存空间以及上下文信息,一个进程挂了不会影响其他进程的运行.这个也是多进程最大的优点,但是它的缺点也很明显. 最大的缺点就是开销很大,创建.销毁进程的开

  • Go并发编程之goroutine使用正确方法

    目录 1. 对创建的gorouting负载 1.1 不要创建一个你不知道何时退出的 goroutine 1.2 不要帮别人做选择 1.3 不要作为一个旁观者 1.4 不要创建不知道什么时候退出的 goroutine 1.5 不要创建都无法退出的 goroutine 1.6 确保创建出的goroutine工作已经完成 2. 总结 3. 参考 并发(concurrency): 指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时

  • Golang 语言控制并发 Goroutine的方法

    goroutine 是 Go语言中的轻量级线程实现,由 Go 运行时(runtime)管理.Go 程序会智能地将 goroutine 中的任务合理地分配给每个 CPU. 01介绍 Golang 语言的优势之一是天生支持并发,我们在 Golang 语言开发中,通常使用的并发控制方式主要有 Channel,WaitGroup 和 Context,本文我们主要介绍一下 Golang 语言中并发控制的这三种方式怎么使用?关于它们各自的详细介绍在之前的文章已经介绍过,感兴趣的读者朋友们可以按需翻阅. 02

  • golang gin 框架 异步同步 goroutine 并发操作

    goroutine机制可以方便地实现异步处理 package main import ( "log" "time" "github.com/gin-gonic/gin" ) func main() { // 1.创建路由 // 默认使用了2个中间件Logger(), Recovery() r := gin.Default() // 1.异步 r.GET("/long_async", func(c *gin.Context) {

  • Go语言中的并发goroutine底层原理

    目录 一.基本概念 ①并发.并行区分 ②从用户态线程,内核态线程阐述go与java并发的优劣 ②高并发为什么是Go语言强项? ③Go语言实现高并发底层GMP模型原理解析 二.上代码学会Go语言并发 ①.开启一个简单的线程 ②.动态的关闭线程 一.基本概念 ①并发.并行区分 1.概念 并发:同一时间段内一个对象执行多个任务,充分利用时间 并行:同一时刻,多个对象执行多个任务 2.图解 类似于超市柜台结账,并行是多个柜台结多个队列,在计算机中是多核cpu处理多个go语言开启的线程,并发是一个柜台结账

  • 深入了解Java语言中的并发性选项有何不同

    前言 Java™ 工程师在努力让并发性容易为开发人员所用.尽管做了不少的改进,但并发性仍然是 Java 平台的一个复杂.容易出错的部分.一部分复杂之处在于理解语言本身中的并发性的低级抽象,这些抽象在您的代码中填满了同步的代码块.另一个复杂之处来自一些新库,比如 fork/join,这些库在某些场景中非常有用,但在其他场景中收效甚微.了解容易混乱的大量低级选项需要专业经验和时间. 脱离 Java 语言的优势之一是,能够改善和简化并发性等区域.每种 Java 下一代语言都为此问题提供了独特的答案,利

  • Go语言中并发的工作原理

    一.Go语言中Goroutine的基本原理 Go语言里的并发指的是能让某个函数独立于其他函数运行的能力. Go语言的goroutine是一个独立的工作单元, Go 语言的并发同步模型来自一个叫作通信顺序进程(Communicating Sequential Processes,CSP)的范型(paradigm). CSP 是一种消息传递模型,通过在goroutine 之间传递数据来传递消息,而不是对数据进行加锁来实现同步访问.消息的传递通过Go语言中的Channel(通道)来实现. 进程(pro

  • Go语言sync.Cond基本使用及原理示例详解

    目录 1. 简介 2. 基本使用 2.1 定义 2.2 方法说明 2.3 使用方式 2.4 使用例子 2.5 为什么Sync.Cond 需要关联一个锁,然后调用Wait方法前需要先获取该锁 3.使用场景 3.1 基本说明 3.2 场景说明 3.2.1 同步和协调多个协程之间共享资源 3.2.2 需要重复唤醒的场景中使用 4. 原理 4.1 基本原理 4.2 实现 4.2.1 Wait方法实现 4.2.2 Singal方法实现 4.2.3 Broadcast方法实现 5.使用注意事项 5.1 调用

  • Go语言上下文context底层原理

    目录 1. context 介绍 2. 基本介绍 3. 源码分析 3.1 Context 接口 3.2 emptyCtx 3.3 cancelCtx 3.4 timerCtx 3.5 valueCtx 4. 使用建议 1. context 介绍 很多时候,我们会遇到这样的情况,上层与下层的goroutine需要同时取消,这样就涉及到了goroutine间的通信.在Go中,推荐我们以通信的方式共享内存,而不是以共享内存的方式通信.所以,就需要用到channl,但是,在上述场景中,如果需要自己去处理

  • Go语言底层原理互斥锁的实现原理

    目录 Go 互斥锁的实现原理? 概念 使用场景 底层实现结构 操作 加锁 解锁 Go 互斥锁正常模式和饥饿模式的区别? 正常模式(非公平锁) 饥饿模式(公平锁) Go 互斥锁允许自旋的条件? Go 互斥锁的实现原理? Go sync包提供了两种锁类型:互斥锁sync.Mutex 和 读写互斥锁sync.RWMutex,都属于悲观锁. 概念 Mutex是互斥锁,当一个 goroutine 获得了锁后,其他 goroutine 不能获取锁(只能存在一个写者或读者,不能同时读和写) 使用场景 多个线程

  • Java并发编程深入理解之Synchronized的使用及底层原理详解 下

    目录 一.synchronized锁优化 1.自旋锁与自适应自旋 2.锁消除 逃逸分析: 3.锁粗化 二.对象头内存布局 三.synchronized锁的膨胀升级过程 1.偏向锁 2.轻量级锁 3.重量级锁 4.各种锁的优缺点 接着上文<Java并发编程深入理解之Synchronized的使用及底层原理详解 上>继续介绍synchronized 一.synchronized锁优化 高效并发是从JDK 5升级到JDK 6后一项重要的改进项,HotSpot虚拟机开发团队在这个版本上花费了大量的资源

  • Java并发编程深入理解之Synchronized的使用及底层原理详解 上

    目录 一.线程安全问题 1.临界资源 2.线程安全问题 3.如何解决线程安全问题 二.synchronized使用介绍 三.synchronized实现原理 1.synchronized底层指令:monitorenter和monitorexit 2.Object Monitor(监视器锁)机制 一.线程安全问题 1.临界资源 多线程编程中,有可能会出现多个线程同时访问同一个共享.可变资源的情况,这个资源我们称之其为临界资源:这种资源可能是:对象.变量.文件等. 共享:资源可以由多个线程同时访问

  • 详解go语言中并发安全和锁问题

    首先可以先看看这篇文章,对锁有些了解 [锁]详解区分 互斥锁.⾃旋锁.读写锁.乐观锁.悲观锁 Mutex-互斥锁 Mutex 的实现主要借助了 CAS 指令 + 自旋 + 信号量 数据结构: type Mutex struct { state int32 sema uint32 } 上述两个加起来只占 8 字节空间的结构体表示了 Go语言中的互斥锁 状态: 在默认情况下,互斥锁的所有状态位都是 0,int32 中的不同位分别表示了不同的状态: 1位表示是否被锁定 1位表示是否有协程已经被唤醒 1

随机推荐