GoLong的学习之路(二十一)进阶,语法之并发(go最重要的特点)(协程的主要用法)

news2024/11/17 9:56:54

并发编程在当前软件领域是一个非常重要的概念,随着CPU等硬件的发展,我们无一例外的想让我们的程序运行的快一点、再快一点。Go语言在语言层面天生支持并发,充分利用现代CPU的多核优势,这也是Go语言能够大范围流行的一个很重要的原因。

并且在云的大放光彩的今天。想要支持分布式的,并且并发。那么go就是不二人选。

当然对于并发来说,一章是难说完的

文章目录

  • 基本概念
    • 串行、并发与并行
    • 进程、线程和协程
    • 并发模型
  • goroutine(正文)
    • go关键字
    • 启动单个goroutine
    • 启动多个goroutine
    • 内存分配机制:动态栈
    • goroutine调度

基本概念

串行、并发与并行

吃糖葫芦:

  • 串行:我们先吃最上面的,一块一块的吃,然后慢慢吃完

  • 并发:同一时间段内执行多个任务(你吃的比较快,在同一时间,别人吃一个,你可以吃两个或者更多)。

  • 并行:同一时刻执行多个任务(你叫朋友一起帮你吃)。

进程、线程和协程

  • 进程(process):程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。

  • 线程(thread):操作系统基于进程开启的轻量级进程,是操作系统调度执行的最小单位。

  • 协程(coroutine):非操作系统提供而是由用户自行创建和控制的用户态‘线程’,比线程更轻量级。

并发模型

行业内,将如何实现并发编程总结归纳为各式各样的并发模型,常见的并发模型有以下几种:

  • 线程&锁模型
  • Actor模型
  • CSP模型
  • Fork&Join模型

Go语言中的并发程序主要是通过基于CSP(communicating sequential processes)goroutinechannel来实现,当然也支持使用传统的多线程共享内存的并发方式(java 的方式,也就是线程&锁模型)。


goroutine(正文)

Goroutine 是 Go 语言支持并发的核心,在一个Go程序中同时创建成百上千个goroutine是非常普遍的,一个goroutine会以一个很小的栈开始其生命周期,一般只需要2KB

区别于操作系统线程系统内核进行调度, goroutine 是由Go运行时(runtime)负责调度

例如

Go运行时会智能地将 m个goroutine 合理地分配给n个操作系统线程,实现类似m:n的调度机制,不再需要Go开发者自行在代码层面维护一个线程池

在Go语言编程中你不需要去自己写进程、线程、协程,你的技能包里只有一个技能——goroutine。(这个就比较方便)

当你需要让某个任务并发执行的时候,你只需要把这个任务包装成一个函数,开启一个 goroutine 去执行这个函数就可以了,就是这么简单粗暴。

其实说到这个我说一下体外话。编程语言趋于简单易操作化已经非常明显了。从市场的角度来说。编程语言的已经从院士–》博士–》–》研究生–》本科生–》专科生–》中学生—》小学生–》幼儿园了。几乎随着时间的变化,越来越来下放。学习成本越来越低。

也就是说,对于编程语言来说。如何扩大行业人选来说。越简单,去学习的人越多,当然这是在这个编程语言有特点的来说。如果没有优势,然后去学,要我说这样纯属扯淡。为了简单而失去编程的本质,就有问题了。

不扯了。。。。。

go关键字

Go语言中使用 goroutine 非常简单,只需要在函数或方法调用前加上go关键字就可以创建一个goroutine,从而让该函数或方法在新创建的 goroutine 中执行。

go f()  // 创建一个新的 goroutine 运行函数f

匿名函数也支持使用go关键字创建 goroutine 去执行

go func(){
  // ...
}()

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

记住在这个大括号后的小括号中填入的传入方法的参数

启动单个goroutine

启动 goroutine 的方式非常简单,只需要在调用函数(普通函数和匿名函数)前加上一个go关键字

先实现一个串行例子

package main

import (
	"fmt"
)

func hello() {
	fmt.Println("hello")
}

func main() {
	hello()
	fmt.Println("你好")
}

根据代码逻辑就是从上至下执行。
在这里插入图片描述

ok我们试一下加上关键字go,启动一个 goroutine 去执行 hello 这个函数。

package main

import (
	"fmt"
	"time"
)

func hello() {
	fmt.Println("hello")
}

func main() {
	go hello()
	fmt.Println("你好")
}

打印的结果居然是:
在这里插入图片描述
ok出bug了。为什么呢?
原因
其实在 Go 程序启动时,Go 程序就会为 main 函数创建一个默认的 goroutine

在上面的代码中我们在 main 函数中使用 go 关键字创建了另外一个 goroutine 去执行 hello 函数,而此时 main goroutine 还在继续往下执行,我们的程序中此时存在两个并发执行的 goroutine。

当 main 函数结束时整个程序也就结束了,同时 main goroutine 也结束了,所有由 main goroutine 创建的 goroutine 也会一同退出。

也就是说我们的 main 函数退出太快,另外一个 goroutine 中的函数还未执行完程序就退出了,导致未打印出“hello”。

所以我们要想办法让 main 函数‘“等一等”将在另一个 goroutine 中运行的 hello 函数。

其中最简单粗暴的方式就是在 main 函数中“time.Sleep”一秒钟了(其实存在等待函数的)

package main

import (
	"fmt"
	"time"
)

func hello() {
	fmt.Println("hello")
}

func main() {
	go hello()
	fmt.Println("你好")
	time.Sleep(time.Second)
}

在这里插入图片描述
此时就成功了。但是者子其中有一个问题发现没有,为什么会先打印 “你好” 呢?

在这里插入图片描述

因为在程序中创建 goroutine 执行函数需要一定的开销,而与此同时 main 函数所在的 goroutine 是继续执行的。

上面说了,这么粗暴的使用。是非常不雅观的。

Go 语言中通过sync包为我们提供了一些常用的并发原语。下一章说如何用。

这里我们先说, sync 包中的WaitGroup。

当你并不关心并发操作的结果或者有其它方式收集并发操作的结果时,WaitGroup是实现等待一组并发操作完成的好方法。

例子:

package main

import (
	"fmt"
	"sync"
)

// 声明全局等待组变量
var wg sync.WaitGroup

func hello() {
	fmt.Println("hello")
	wg.Done() // 告知当前goroutine完成
}

func main() {
	wg.Add(1) // 登记1个goroutine
	go hello()
	fmt.Println("你好")
	wg.Wait() // 阻塞等待登记的goroutine完成
}

将代码编译后再执行,得到的输出结果和之前一致,但是这一次程序不再会有多余的停顿,hello goroutine 执行完毕后程序直接退出。

启动多个goroutine

单个并发只能说是小试牛刀,多个并发才是业务该有的逻辑。

package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup

func hello(i int) {
	defer wg.Done() // goroutine结束就登记-1
	fmt.Println("hello", i)
}
func main() {
	for i := 0; i < 10; i++ {
		wg.Add(1) // 启动一个goroutine就登记+1
		go hello(i)
	}
	wg.Wait() // 等待所有登记的goroutine都结束
}

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

但是看了这个,大家对于这个defer起的作用肯定是有疑问的。

在很早之前就说过这个关键字。这里我认为有必要在给大家说一说。这个关键字很重要,它的应用场景很多。在go中最主要的为三个,一个是同步并发(这里的主要作用),一个是搭配recover 处理异常,一个是关闭资源。这三个应用场景非常常见。而有这些场景,离不开它本有的特性。-----------延迟调用,简单而言就是最后执行。

内存分配机制:动态栈

操作系统的线程一般都有固定的栈内存(通常为2MB),而 Go 语言中的 goroutine 非常轻量级,一个 goroutine初始栈空间很小(一般为2KB),所以在 Go 语言中一次创建数万个 goroutine 也是可能的。并且 goroutine 的栈不是固定的,可以根据需要动态地增大或缩小, Go 的 runtime 会自动为 goroutine 分配合适的栈空间

goroutine调度

操作系统内核在调度时

  1. 会挂起当前正在执行的线程并将寄存器中的内容保存到内存中
  2. 选出接下来要执行的线程并从内存中恢复该线程的寄存器信息
  3. 恢复执行该线程的现场并开始执行线程

从一个线程切换到另一个线程需要完整的上下文切换。因为可能需要多次内存访问,索引这个切换上下文的操作开销较大,会增加运行的cpu周期。

goroutine 的调度

goroutine 的调度是Go语言运行时(runtime)层面的实现,是完全由 Go 语言本身实现的一套调度系统——go scheduler。它的作用是按照一定的规则将所有的 goroutine 调度到操作系统线程上执行。

目前 Go 语言的调度器采用的是 GPM 调度模型。
在这里插入图片描述

  • G:表示 goroutine,每执行一次go f()就创建一个 G,包含要执行的函数和上下文信息。

  • 全局队列(Global Queue):存放等待运行的 G。

  • P:表示 goroutine 执行所需的资源,最多有 GOMAXPROCS 个。

  • P 的本地队列:同全局队列类似,存放的也是等待运行的G,存的数量有限,不超过256个。新建 G 时,G 优先加入到 P 的本地队列,如果本地队列满了会批量移动部分 G 到全局队列。

  • M:线程想运行任务就得获取 P,从 P 的本地队列获取 G,当 P 的本地队列为空时,M 也会尝试从全局队列或其他 P 的本地队列获取 G。M 运行 G,G 执行之后,M 会从 P 获取下一个 G,不断重复下去。

  • Goroutine 调度器操作系统调度器是通过 M 结合起来的,每个 M 都代表了1个内核线程,操作系统调度器负责把内核线程分配到 CPU 的核上执行。

  • GOMAXPROCS:Go运行时的调度器使用GOMAXPROCS参数来确定需要使用多少个 OS 线程来同时执行 Go 代码。

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

默认值是机器上的 CPU 核心数。例如在一个 8 核心的机器上,GOMAXPROCS 默认为 8。

Go语言中可以通过runtime.GOMAXPROCS函数设置当前程序并发时占用的 CPU逻辑核心数。

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

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

相关文章

AI 视频 | 文本生视频工具又迎来重大更新,Runway Gen-2 到底有多强?Gen-2 怎么用(保姆级教程)

一、引言 不久前刚介绍了一个号称地表最强的文本生视频的工具 Moonvalley&#xff1a;免费的 AI 视频生成工具 Moonvalley 厉害了&#xff01;Moonvalley 怎么用&#xff08;保姆级教程&#xff09; 紧接着在 11 月 2 日&#xff0c;Runway 重磅发布了第 2 代文本到视频和图像…

unity打AB包,AssetBundle预制体与图集(三)

警告&#xff1a; spriteatlasmanager.atlasrequested wasn’t listened to while 条件一&#xff1a;图片打图集里面去了 条件二&#xff1a;然后图集打成AB包了 条件三&#xff1a;UI预制体也打到AB包里面去了 步骤一&#xff1a;先加载了图集 步骤二&#xff1a;再加载UI预…

测试面试题集锦(四)| Linux 与 Python 编程篇(附答案)

本系列文章总结归纳了一些软件测试工程师常见的面试题&#xff0c;主要来源于个人面试遇到的、网络搜集&#xff08;完善&#xff09;、工作日常讨论等&#xff0c;分为以下十个部分&#xff0c;供大家参考。如有错误的地方&#xff0c;欢迎指正。有更多的面试题或面试中遇到的…

四川思维跳动商务信息咨询有限公司可靠吗?

随着抖音等短视频平台的兴起&#xff0c;越来越多的商家开始利用这些平台进行带货。四川思维跳动商务信息咨询有限公司也提供抖音带货服务&#xff0c;那么&#xff0c;他们的服务可靠吗&#xff1f;本文将为你揭开真相&#xff0c;让你安心选择&#xff01; 一、公司简介 四川…

分布式任务调度(04)--自研

1 背景 兼容技术团队自研的RPC框架&#xff0c;技术团队不需要修改代码&#xff0c;RPC注解方法可以托管在任务调度系统中&#xff0c;直接当做一个任务来执行。 研读XXL-JOB&#xff0c;同时从阿里云分布式任务调度 SchedulerX 吸取。 SchedulerX 1.0 架构图 Schedulerx-co…

MCU常见通信总线串讲(二)—— RS232和RS485

&#x1f64c;秋名山码民的主页 &#x1f602;oi退役选手&#xff0c;Java、大数据、单片机、IoT均有所涉猎&#xff0c;热爱技术&#xff0c;技术无罪 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; 获取源码&#xff0c;添加WX 目录 前言一…

Word文档中书签使用注意事项

在协同编辑文档时&#xff0c;书签被广泛应用在各种场景中&#xff0c;当我们在编辑时有时候会搞不懂我们的哪些操作会导致书签更换段落或是删除&#xff0c;比如同样的某些操作在修订场景下书签就不会消失&#xff0c;但非修订状态就会消失。在这篇文章中我们就介绍一下我们的…

数据中心的防雷接地

在数据中心建设中&#xff0c;防雷接地是非常重要的一项工作&#xff0c;能有效地保护数据中心设备和系统免受雷击的影响。下面是一些关于数据中心防雷接地的常见做法&#xff1a; 接地系统设计&#xff1a;在数据中心的设计阶段&#xff0c;应考虑到接地系统的布置和规划。为了…

Halcon如何使用SaperaLT库连接dalsa相机

halcon安装好的时候&#xff0c;没有带SaperaLT的采集库&#xff0c;需要额外在Halcon官网下载此库。 以下是halcon官网下载此库的链接。官网需要注册才可以下载。 https://www.mvtec.com/downloads/interfaces?tx_mvtecproduct_extensiondownloadlist%5Bfilter%5D%5B0%5Dma…

时空智友企业信息管理系统任意文件读取漏洞复现

简介 时空智友企业信息管理系统是一个用于企业流程管理和控制的软件系统。它旨在帮助企业实现流程的规范化、自动化和优化&#xff0c;从而提高工作效率、降低成本并提升管理水平。 时空智友企业信息管理系统存在任意文件读取漏洞&#xff0c;攻击者可以在未授权的情况下读取…

如何在word文档中批量插入二维码

合同系统中&#xff0c;一般流程是线上拟稿、审批、定稿&#xff0c;然后线下打印定稿的合同并且存档。当拿到一个纸质合同&#xff0c;需要去线上系统查询当时的合同拟制过程&#xff0c;那如何快速定位到这个文档&#xff0c;是一个问题。通用的做法是&#xff0c;将该文档的…

yolov5 学习体验

模型训练&#xff1a; train.py def parse_opt(knownFalse):parser argparse.ArgumentParser()parser.add_argument(--weights, typestr, defaultROOT / yolov5s.pt, helpinitial weights path)parser.add_argument(--cfg, typestr, default, helpmodel.yaml path)parser.ad…

FL Studio21中文升级版全能的音乐制作软件

对于唱作人来说&#xff0c;一款优秀、全能的音乐制作软件&#xff0c;能帮助他们在创作上获得更多的灵感、以及为歌曲带来更多的变化。 FL Cloud 音效库包含开放版权的Loop和采样&#xff0c;以及来自 FL Studio 著名用户的艺术家独家内容。更新后&#xff0c;现在还可以使用…

必看!玩转Salesforce沙盒的5个实用技巧

定期刷新沙盒对于尝试最新版本的功能&#xff0c;以及防止在生产组织的环境中缺乏测试而导致开发工作回滚至关重要。 为了确保沙盒设置在刷新后顺利进行&#xff0c;需要考虑几个因素。首先&#xff0c;确保有完善的文档化流程。文档应分为Conga、DocuSign、数据&#xff08;C…

基于ubuntu1604的ROS安装

不同版本的Ubuntu都有对应的ROS版本&#xff0c;不要强行安装不对应的版本&#xff0c;否则遇到问题会很难找到解决方法。此教程也只是基于Ubuntu1604和kinetic版本的ROS。 一、基本流程 以下命令仅记录执行顺序&#xff0c;不要无脑复制执行&#xff0c;重在理解 #基本更新…

JavaScript 进阶问题列表,巩固自己的知识。

不定时更新 JavaScript 进阶问题列表 从基础到进阶&#xff0c;测试你有多了解 JavaScript&#xff0c;刷新你的知识&#xff0c;或者帮助你的 coding 面试&#xff01; &#x1f4aa; &#x1f680; 答案❤️ 1. 输出是什么&#xff1f; function sayHi() {console.log(na…

【Git】Git 学习笔记_操作本地仓库

1. 安装与初始化配置 1.1 安装 下载地址 在文件夹里右键点击 git bash here 即可打开命令行面板。 git -v // 查看版本1.2 配置 git config --global user.name "heo" git config --global user.email xxxgmail.com git config --global credential.helper stor…

关于Alibaba Cloud Toolkit 下载配置以及后端自动部署

idea中File-Settings-Plugins 搜索Alibaba Cloud Toolkit点击下载&#xff0c;下载完成重启 1、点击 Tools-Alibaba Cloud-Deploy to Host 部署到主机 2、配置服务器ip、jar包启动命令、服务器jar存放位置 3、设置服务器ip用户名密码&#xff0c;点击测试连接情况 4、配置脚本…

基于SSM的酒店客房管理系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

读取xlsx文件存入MongoDB数据库中

要将xlsx文件存入MongoDB数据库&#xff0c;您需要执行以下步骤&#xff1a; 步骤1&#xff1a;安装必要的库 您需要安装pymongo和openpyxl库。您可以使用以下命令安装&#xff1a; pip install pymongo openpyxl 步骤2&#xff1a;创建一个MongoDB数据库 您需要创建一个Mon…