go并发编程-介绍与Goroutine使用

news2024/11/27 4:35:44

1. 并发介绍

进程和线程

    A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。
    B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
    C.一个进程可以创建和撤销多个线程;同一个进程中的多个线程之间可以并发执行。

并发和并行

    A. 多线程程序在一个核的cpu上运行,就是并发。
    B. 多线程程序在多个核的cpu上运行,就是并行。

并发

并发

并行

并行

协程和线程

协程:独立的栈空间,共享堆空间,调度由用户自己控制,本质上有点类似于用户级线程,这些用户级线程的调度也是自己实现的。
线程:一个线程上可以跑多个协程,协程是轻量级的线程。

goroutine 只是由官方实现的超级"线程池"。

每个实力4~5KB的栈内存占用和由于实现机制而大幅减少的创建和销毁开销是go高并发的根本原因。

并发不是并行:

并发主要由切换时间片来实现"同时"运行,并行则是直接利用多核实现多线程的运行,go可以设置使用核数,以发挥多核计算机的能力。

goroutine 奉行通过通信来共享内存,而不是共享内存来通信。

2. Goroutine

在java/c++中我们要实现并发编程的时候,我们通常需要自己维护一个线程池,并且需要自己去包装一个又一个的任务,同时需要自己去调度线程执行任务并维护上下文切换,这一切通常会耗费程序员大量的心智。

那么能不能有一种机制,程序员只需要定义很多个任务,让系统去帮助我们把这些任务分配到CPU上实现并发执行呢?

Go语言中的goroutine就是这样一种机制,goroutine的概念类似于线程,但 goroutine是由Go的运行时(runtime)调度和管理的。Go程序会智能地将 goroutine 中的任务合理地分配给每个CPU。Go语言之所以被称为现代化的编程语言,就是因为它在语言层面已经内置了调度和上下文切换的机制。

在Go语言编程中你不需要去自己写进程、线程、协程,你的技能包里只有一个技能–goroutine,当你需要让某个任务并发执行的时候,你只需要把这个任务包装成一个函数,开启一个goroutine去执行这个函数就可以了,就是这么简单粗暴。

2.1 使用goroutine

Go语言中使用goroutine非常简单,只需要在调用函数的时候在前面加上go关键字,就可以为一个函数创建一个goroutine。

一个goroutine必定对应一个函数,可以创建多个goroutine去执行相同的函数。

启动单个goroutine

func hello() {
    fmt.Println("Hello Goroutine!")
}
func main() {
    hello()
    fmt.Println("main goroutine done!")
}

这个示例中hello函数和下面的语句是串行的,执行的结果是打印完Hello Goroutine!后打印main goroutine done!。

启动一个goroutine去执行hello这个函数。

func main() {
    go hello() // 启动另外一个goroutine去执行hello函数
    fmt.Println("main goroutine done!")
}

这一次的执行结果只打印了main goroutine done!,并没有打印Hello Goroutine!。为什么呢?

在程序启动时,Go程序就会为main()函数创建一个默认的goroutine。

当main()函数返回的时候该goroutine就结束了,所有在main()函数中启动的goroutine会一同结束,main函数所在的goroutine就像是权利的游戏中的夜王,其他的goroutine都是异鬼,夜王一死它转化的那些异鬼也就全部GG了。简单解释:如果主协程退出了,其他任务就死了

所以我们要想办法让main函数等一等hello函数,最简单粗暴的方式就是time.Sleep了。

func main() {
    go hello() // 启动另外一个goroutine去执行hello函数
    fmt.Println("main goroutine done!")
    time.Sleep(time.Second)
}

执行上面的代码你会发现,这一次先打印main goroutine done!,然后紧接着打印Hello Goroutine!。

首先为什么会先打印main goroutine done!是因为我们在创建新的goroutine的时候需要花费一些时间,而此时main函数所在的goroutine是继续执行的。

启动多个goroutine

在Go语言中实现并发就是这样简单,我们还可以启动多个goroutine。让我们再来一个例子: (这里使用了sync.WaitGroup来实现goroutine的同步)

var wg sync.WaitGroup

func hello(i int) {
    defer wg.Done() // goroutine结束就登记-1
    fmt.Println("Hello Goroutine!", i)
}
func main() {

    for i := 0; i < 10; i++ {
        wg.Add(1) // 启动一个goroutine就登记+1
        go hello(i)
    }
    wg.Wait() // 等待所有登记的goroutine都结束
}

多次执行上面的代码,会发现每次打印的数字的顺序都不一致。这是因为10个goroutine是并发执行的,而goroutine的调度是随机的。

2.2. goroutine与线程

可增长的栈

OS线程(操作系统线程)一般都有固定的栈内存(通常为2MB),一个goroutine的栈在其生命周期开始时只有很小的栈(典型情况下2KB),goroutine的栈不是固定的,他可以按需增大和缩小,goroutine的栈大小限制可以达到1GB,虽然极少会用到这个大。所以在Go语言中一次创建十万左右的goroutine也是可以的。

goroutine调度

GPM是Go语言运行时(runtime)层面的实现,是go语言自己实现的一套调度系统。区别于操作系统调度OS线程。

  • 1.G很好理解,就是个goroutine的,里面除了存放本goroutine信息外 还有与所在P的绑定等信息。
  • 2.P管理着一组goroutine队列,P里面会存储当前goroutine运行的上下文环境(函数指针,堆栈地址及地址边界),P会对自己管理的goroutine队列做一些调度(比如把占用CPU时间较长的goroutine暂停、运行后续的goroutine等等)当自己的队列消费完了就去全局队列里取,如果全局队列里也消费完了会去其他P的队列里抢任务。
  • 3.M(machine)是Go运行时(runtime)对操作系统内核线程的虚拟, M与内核线程一般是一一映射的关系, 一个groutine最终是要放到M上执行的;

P与M一般也是一一对应的。他们关系是: P管理着一组G挂载在M上运行。当一个G长久阻塞在一个M上时,runtime会新建一个M,阻塞G所在的P会把其他的G 挂载在新建的M上。当旧的G阻塞完成或者认为其已经死掉时 回收旧的M。

P的个数是通过runtime.GOMAXPROCS设定(最大256),Go1.5版本之后默认为物理线程数。 在并发量大的时候会增加一些P和M,但不会太多,切换太频繁的话得不偿失。

单从线程调度讲,Go语言相比起其他语言的优势在于OS线程是由OS内核来调度的,goroutine则是由Go运行时(runtime)自己的调度器调度的,这个调度器使用一个称为m:n调度的技术(复用/调度m个goroutine到n个OS线程)。 其一大特点是goroutine的调度是在用户态下完成的, 不涉及内核态与用户态之间的频繁切换,包括内存的分配与释放,都是在用户态维护着一块大的内存池, 不直接调用系统的malloc函数(除非内存池需要改变),成本比调度OS线程低很多。 另一方面充分利用了多核的硬件资源,近似的把若干goroutine均分在物理线程上, 再加上本身goroutine的超轻量,以上种种保证了go调度方面的性能。

参考文章:

go Goroutine介绍 - 范斯猫 (fansimao.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1425946.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

排序之计数排序

꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN …

Java基础 集合(一)Collection与Iterator Iterable迭代器详解以及集合在for循环下如何删除

目录 简介 Iterator和Iterable for循环删除问题 for-each下删除数据 解决方法 for下删除数据 解决方法 前言-与正文无关 生活远不止眼前的苦劳与奔波&#xff0c;它还充满了无数值得我们去体验和珍惜的美好事物。在这个快节奏的世界中&#xff0c;我们往往容易陷入工作的…

非鸿蒙官方低代码源码生成器

介绍 鸿蒙低代码可视化开发神器快速对鸿蒙ArkUI生成源码&#xff0c;结合类似小程序类似设计&#xff0c;页面设计底部菜单&#xff0c;支持宫格组件、轮播图、图文列表、图片组件、文本内容组件&#xff0c;快速对接第三方HttpApi。通过鸿蒙扩展axios扩展库加载数据源&#x…

众星素颜出镜影协大会,真实状态引发热议,陈道明备受赞誉。

♥ 为方便您进行讨论和分享&#xff0c;同时也为能带给您不一样的参与感。请您在阅读本文之前&#xff0c;点击一下“关注”&#xff0c;非常感谢您的支持&#xff01; 文 |猴哥聊娱乐 编 辑|徐 婷 校 对|侯欢庭 岁末之际&#xff0c;电影家协会盛大集结&#xff0c;众星璀璨…

[JAVA数据结构] 二叉树 - 详解

目录 (一) 树形结构 1. 树的概念与结构 2. 树的表示形式 (二) 二叉树 1. 二叉树的概念 2. 两种特殊的二叉树 3. 二叉树的性质 4. 二叉树的存储 5. 二叉树的遍历 (1) 前序遍历 (2)中序遍历 (3)后序遍历 6. 二叉树的基本操作 - 模拟实现 (一) 树形结构 1. 树的概念与…

机器学习算法决策树

决策树的介绍 决策树是一种常见的分类模型&#xff0c;在金融风控、医疗辅助诊断等诸多行业具有较为广泛的应用。决策树的核心思想是基于树结构对数据进行划分&#xff0c;这种思想是人类处理问题时的本能方法。例如在婚恋市场中&#xff0c;女方通常会先询问男方是否有房产&a…

c/c++静态字符串的基本操作

文章目录 1.串的定义2. 赋值操作3.字符串复制操作4.判断空5.比较操作6.求串的长度7.打印全部元素8.求字串9.串拼接10.清空操作11.源代码 在本篇博客中&#xff0c;定义的字符串为索引1为起始下标。 1.串的定义 #define MAXLEN 255 //预定义最大串长255 typedef struct {char …

从[redis:LinkedList]中学习链表

文章目录 adlistlistNodelistmacros[宏定义]listCreatelistInitNodelistEmptylistReleaselistAddNodeHeadlistLinkNodeHeadlistAddNodeTaillistLinkNodeTaillistInsertNodelistDelNodelistUlinkNodelistIndexredis3.2.100quicklistredis7.2.2quicklist redis的基本数据类型之一…

【代码随想录】LC 77. 组合

文章目录 前言一、题目1、原题链接2、题目描述 二、解题报告1、思路分析2、代码详解 前言 本专栏文章为《代码随想录》书籍的刷题题解以及读书笔记&#xff0c;如有侵权&#xff0c;立即删除。 一、题目 1、原题链接 77. 组合 2、题目描述 二、解题报告 1、思路分析 &#x…

C++类和对象(中)六个默认成员函数

&#x1f308;类的六个默认成员函数 任何一个类&#xff0c;不管是否为空&#xff0c;都会在生成的时候默认调用六个成员函数&#xff0c;这些成员函数可以自动生成&#xff0c;也可以由程序员写出。这六个默认成员函数分别是&#xff1a; 最主要的是前四个&#xff1a; 初始…

2-树-恢复二叉搜索树

这是树的第二篇算法&#xff0c;力扣链接。 给你二叉搜索树的根节点 root &#xff0c;该树中的 恰好 两个节点的值被错误地交换。请在不改变其结构的情况下&#xff0c;恢复这棵树 。 示例 1&#xff1a; 输入&#xff1a;root [1,3,null,null,2] 输出&#xff1a;[3,1,null,…

Google Gemini Pro 国内版

Google Gemini Pro 国内版&#xff1a;【直达链接】 Google Gemini Pro 国内版 能力分类基准测试描述更高分数更好Gemini UltraGPT-4通用MMLU57个主题&#xff08;包括STEM、人文等&#xff09;的问题表示是90.0%86.4%&#xff08;5-shot, 报告&#xff09;推理Big-Bench Hard…

linux系统上C程序的编译、运行及调试-gcc

gcc -o timer timer.c &#xff1a;生成可执行文件main&#xff0c;依托main.c,也可依托多个文件./timer :运行代码

C# 根据USB设备VID和PID 获取设备总线已报告设备描述

总线已报告设备描述 DEVPKEY_Device_BusReportedDeviceDesc 模式 winform 语言 c# using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Window…

【软件设计师笔记】计算机系统基础知识考点

&#x1f413; 计算机系统组成 计算机系统是由硬件和软件组成的&#xff0c;它们协同工作来运行程序。计算机的基本硬件系统由 运算器、控制器、存储器、输入设备和输出设备5大部件组成。运算器、控制器等部件被集成 在一起统称为中央处理单元&#xff08;Central Processing …

幻兽帕鲁服务器怎么更新?教你一行命令搞定!

今天发现幻兽帕鲁又进行了一次更新&#xff0c;所以我们的服务器也是需要同步更新的 那么&#xff0c;我们通过阿里云或腾讯云一键部署的幻兽帕鲁服务器怎么更新呢&#xff1f; 腾讯云轻量云一键部署幻兽帕鲁服务器教程&#xff1a;https://curl.qcloud.com/pzBO9wN7 先来讲…

短视频去水印教程,免费一键获取视频、图片、文案【迅风去水印】

自媒体行业的蓬勃发展&#xff0c;让越来越多的创作者涌入其中。然而&#xff0c;剪辑过程中常常遭遇到一个令人头疼的问题&#xff0c;那就是视频或图片上的水印。这些水印不仅会影响到作品的美感&#xff0c;还可能侵犯到版权。为了帮大家解决这一难题&#xff0c;分享一个免…

HTML 样式学习手记

HTML 样式学习手记 在探索网页设计的世界时&#xff0c;我发现HTML元素的样式调整真的是个很酷的环节。通过简单的属性设置&#xff0c;就能让文字换上五彩斑斓的颜色、变换各异的字体和大小。特别是那个style属性&#xff0c;感觉就像是一扇通往CSS魔法世界的大门。 代码小试…

uniapp微信小程序触底加载(超简单)

你在哪个页面需要就给他在page.json里面填写以下代码&#xff0c;表示距离底部还有50px就触发 1.page.json添加以下代码 "onReachBottonDistance":50 这是文档链接 页面 | uni-app官网 (dcloud.net.cn) 2. 页面中写以下代码 onReachBottom(e) {console.log(&quo…

【Git】01 Git介绍与安装

文章目录 一、版本控制系统二、Git三、Windows安装Git3.1 下载Git3.2 安装3.3 检查 四、Linux安装Git4.1 YUM安装4.2 源码安装 五、配置Git5.1 配置用户名和邮箱5.2 配置级别5.3 查看配置 六、总结 一、版本控制系统 版本控制系统&#xff0c;Version Control System&#xff…