go_并发编程(1)
go并发编程
- 一、 并发介绍
- 1,进程和线程
- 2,并发和并行
- 3,协程和线程
- 4,goroutine
- 二、 Goroutine
- 1,使用goroutine
- 1)启动单个goroutine
- 2)启动多个goroutine
- 2,goroutine与线程
- 3,goroutine调度
- 三、runtime包
- 1,runtime.Gosched()
- 2,runtime.Goexit() 退出当时协程
- 3, runtime.GOMAXPROCS
- 4,将任务分配到不同的CPU逻辑核心上实现并行
- 5,Go语言中的操作系统线程和goroutine的关系:
- 四、channel
- 1,CSP模型
- 2,channel 类型
- 3,创建channel
- 4,channel 操作
- 1)发送
- 2)接收
- 3)关闭
- 5,无缓冲通道
- 6,有缓冲通道
- 7,从通道中遍历获取值
- 8,单向通道
- 8,通道总结
一、 并发介绍
1,进程和线程
- A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。
- B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
- C.一个进程可以创建和撤销多个线程;同一个进程中的多个线程之间可以并发执行
2,并发和并行
- A. 多线程程序在一个核的cpu上运行,就是并发。
- B. 多线程程序在多个核的cpu上运行,就是并行。
并发:

并行:

3,协程和线程
- 协程:独立的栈空间,共享堆空间,调度由用户自己控制,本质上有点类似于用户级线程,这些用户级线程的调度也是自己实现的。
- 线程:一个线程上可以跑多个协程,协程是轻量级的线程。
4,goroutine
- goroutine 只是由官方实现的超级"线程池"。
- 每个实力4~5KB的栈内存占用和由于实现机制而大幅减少的创建和销毁开销是go高并发的根本原因
- goroutine 奉行通过通信来共享内存,而不是共享内存来通信。
二、 Goroutine
Go语言引入了goroutine机制,简化了并发编程。程序员只需定义任务函数,通过开启goroutine实现并发执行,而无需自己管理线程池、任务调度和上下文切换。
Go运行时负责智能分配任务到CPU,将复杂性隐藏在底层。
这使得Go成为现代化编程语言,使并发编程更加简单和高效。
1,使用goroutine
- 在函数,或匿名函数 前面添加 go 关键词。
- 一个goroutine必定对应一个函数,可以创建多个goroutine去执行相同的函数。
1)启动单个goroutine
func hello() {fmt.Println("Hello Goroutine!")
}
func main() {go hello()fmt.Println("main goroutine done!")
}//但打印没有 ,Hello Goroutine! ,因为main 生命周期结束,goroutine 还没启动//让main 等等 goroutine 粗暴方法:func main() {go hello() // 启动另外一个goroutine去执行hello函数fmt.Println("main goroutine done!")time.Sleep(time.Second)
}
2)启动多个goroutine
- 使用了sync.WaitGroup来实现goroutine的同步
var wg sync.WaitGroupfunc hello(i int) {defer wg.Done() // goroutine结束就登记-1fmt.Println("Hello Goroutine!", i)
}
func main() {for i := 0; i < 10; i++ {wg.Add(1) // 启动一个goroutine就登记+1go hello(i)}wg.Wait() // 等待所有登记的goroutine都结束
}打印出来,并不是顺序,因为这是因为10个goroutine是并发执行的,而goroutine的调度是随机的。
2,goroutine与线程
- 可增长栈
OS线程(操作系统线程)一般都有固定的栈内存(通常为2MB),一个goroutine的栈在其生命周期开始时只有很小的栈(典型情况下2KB),goroutine的栈不是固定的,他可以按需增大和缩小,goroutine的栈大小限制可以达到1GB,虽然极少会用到这个大。所以在Go语言中一次创建十万左右的goroutine也是可以的。
3,goroutine调度
Go语言的运行时(runtime)引入了GPM模型来实现并发调度,与传统操作系统调度不同。
-
G(goroutine): G是goroutine的缩写,代表一个任务单元。它存储了该任务的信息,以及与所在P(处理器)的关联。
-
P(处理器): P管理一组goroutine队列,包含当前goroutine的运行上下文。P负责调度自己的队列,比如暂停耗时长的任务、切换到其他任务。当P队列为空,它会从全局队列取任务,甚至从其他P队列抢占任务。
-
M(机器): M是Go运行时对操作系统内核线程的虚拟。通常是一一对应的关系,每个M执行一个goroutine。当一个G长时间阻塞在一个M上,会创建新的M,将其他G挂载在新M上。旧M释放后,用于回收资源。
-
GOMAXPROCS: 用于设定P的个数,控制并发度,但不会过多地增加P和M,以避免频繁切换的开销。
Go语言与其他语言不同之处在于,它在运行时实现了自己的调度器,使用m:n调度技术。这意味着goroutine的调度发生在用户态,避免了内核态与用户态的频繁切换,包括内存分配与释放都在用户态维护,性能开销较小。此外,Go语言充分利用多核硬件资源,将多个goroutine均匀分配在物理线程上,加上goroutine的轻量特性,保证了高效的并发调度性能。
三、runtime包
1,runtime.Gosched()
一种协作式多任务切换的方式,让正在运行的 goroutine 暂时停下来,让其他等待执行的 goroutine 有机会运行。
package mainimport ("fmt""runtime"
)func main() {go func(s string) {for i := 0; i < 5; i++ {fmt.Println(s)}}("world")// 主程for i := 0; i < 2; i++ {//切换 再次分配任务runtime.Gosched()fmt.Println("hello")}
}
2,runtime.Goexit() 退出当时协程
package mainimport ("fmt""runtime"
)func main() {go func() {defer fmt.Println("A.defer")func() {defer fmt.Println("B.defer")// 结束协程runtime.Goexit()defer fmt.Println("C.defer")fmt.Println("B")}()fmt.Println("A")}()for {}
}
3, runtime.GOMAXPROCS
-
Go运行时的调度器使用GOMAXPROCS参数来确定需要使用多少个OS线程来同时执行Go代码。默认值是机器上的CPU核心数。例如在一个8核心的机器上,调度器会把Go代码同时调度到8个OS线程上(GOMAXPROCS是m:n调度中的n)。
-
Go语言中可以通过runtime.GOMAXPROCS()函数设置当前程序并发时占用的CPU逻辑核心数。
-
Go1.5版本之前,默认使用的是单核心执行。Go1.5版本之后,默认使用全部的CPU逻辑核心数。
4,将任务分配到不同的CPU逻辑核心上实现并行
- 单核心
package mainimport ("fmt""runtime""time"
)func a() {for i := 1; i < 10; i++ {fmt.Println("A:", i)}
}func b() {for i := 1; i < 10; i++ {fmt.Println("B:", i)}
}func main() {runtime.GOMAXPROCS(1)go a()go b()time.Sleep(time.Second)
}输出,看出是执行完一个goroutine ,再执行另一个
B: 1
B: 2
B: 3
B: 4
B: 5
B: 6
B: 7
B: 8
B: 9
A: 1
A: 2
A: 3
A: 4
A: 5
A: 6
A: 7
A: 8
A: 9
- 多核心
package mainimport ("fmt""runtime""time"
)func a() {for i := 1; i < 10; i++ {fmt.Println("A:", i)}
}func b() {for i := 1; i < 10; i++ {fmt.Println("B:", i)}
}func main() {runtime.GOMAXPROCS(2)go a()go b()time.Sleep(time.Second)
}输出,看出是并发执行
B: 1
B: 2
B: 3
B: 4
B: 5
B: 6
B: 7
B: 8
A: 1
A: 2
A: 3
A: 4
A: 5
A: 6
A: 7
A: 8
A: 9
B: 9
5,Go语言中的操作系统线程和goroutine的关系:
- 1.一个操作系统线程对应用户态多个goroutine。
- 2.go程序可以同时使用多个操作系统线程。
- 3.goroutine和OS线程是多对多的关系,即m:n
四、channel
1,CSP模型
并发执行函数的目的是让多个任务同时进行,但仅仅并发执行函数是不够的,因为这些函数可能需要相互交换数据。在并发环境中,共享内存虽然可以用于数据交换,但容易引发竞态问题,而使用互斥量会影响性能。
Go语言采用了CSP(Communicating Sequential Processes)并发模型,强调通过通信来共享数据,而不是通过共享数据来进行通信。这种方式更加安全且高效。
-
关键点:
-
CSP模型: Go语言采用了CSP模型,强调通过通信来实现协程(goroutine)间的数据交换,而不是直接共享内存。
-
通道(channel): 通道是用于协程间通信的一种机制,类似于一个队列,保证了数据的顺序性。通过在通道中发送和接收数据,协程可以安全地进行交互。
-
通道的特点: 每个通道都有特定的元素类型,通道的操作遵循先进先出原则。通过通道的发送和接收操作,协程之间可以安全地进行数据交换,避免了竞态问题。
-
并发优势: 通过通道,Go语言实现了安全且高效的并发编程,允许协程在不同任务之间进行数据交换,而不需要显式地使用互斥量进行加锁。
-
通过使用通道,Go语言的并发模型强调了协程之间通过通信共享数据,而不是通过共享数据来进行通信,从而避免了许多传统并发模型中常见的问题。这使得并发编程更加安全、简洁和高效。
2,channel 类型
- channel 是一种类型,引用类型
声明格式:var 变量 chan 元素类型
例如:var ch1 chan int // 声明一个传递整型的通道var ch2 chan bool // 声明一个传递布尔型的通道var ch3 chan []int // 声明一个传递int切片的通道
3,创建channel
通道是引用类型,通道类型的空值是nil。var ch chan int
fmt.Println(ch) // <nil>声明的通道后需要使用make函数初始化之后才能使用。创建channel的格式如下:make(chan 元素类型, [缓冲大小]) // 缓冲大小可选例如:
ch4 := make(chan int)
ch5 := make(chan bool)
ch6 := make(chan []int)
4,channel 操作
1)发送
ch <- 10 // 把10发送到ch中
2)接收
x := <- ch // 从ch中接收值并赋值给变量x
<-ch // 从ch中接收值,忽略结果
3)关闭
close(ch)
- 只有在通知接收方goroutine所有的数据都发送完毕的时候才需要关闭通道。
- 通道是可以被垃圾回收机制回收的,它和关闭文件是不一样的,在结束操作之后关闭文件是必须要做的,但关闭通道不是必须的。
注意:
1.对一个关闭的通道再发送值就会导致panic。2.对一个关闭的通道进行接收会一直获取值直到通道为空。3.对一个关闭的并且没有值的通道执行接收操作会得到对应类型的零值。4.关闭一个已经关闭的通道会导致panic。
5,无缓冲通道
func main() {ch := make(chan int)ch <- 10fmt.Println("发送成功")
}//出现以下错误fatal error: all goroutines are asleep - deadlock!goroutine 1 [chan send]:main.main().../src/github.com/pprof/studygo/day06/channel02/main.go:8 +0x54
- 无缓冲的通道必须有接收才能发送。
- 上面的代码会阻塞在ch <- 10这一行代码形成死锁
启动一个goroutine 解决该问题:
func recv(c chan int) {ret := <-cfmt.Println("接收成功", ret)
}
func main() {ch := make(chan int)go recv(ch) // 启用goroutine从通道接收值ch <- 10fmt.Println("发送成功")
}
无缓冲通道上的发送操作会阻塞,直到另一个goroutine在该通道上执行接收操作,这时值才能发送成功,两个goroutine将继续执行。相反,如果接收操作先执行,接收方的goroutine将阻塞,直到另一个goroutine在该通道上发送一个值。
使用无缓冲通道进行通信将导致发送和接收的goroutine同步化。因此,无缓冲通道也被称为同步通道。
6,有缓冲通道
func main() {ch := make(chan int, 1) // 创建一个容量为1的有缓冲区通道ch <- 10fmt.Println("发送成功")
}
- 只要通道的容量大于零,那么该通道就是有缓冲的通道,通道的容量表示通道中能存放元素的数量。
- 可以使用内置的len函数获取通道内元素的数量,使用cap函数获取通道的容量。
7,从通道中遍历获取值
package mainimport "fmt"func main() {ch1 := make(chan int)ch2 := make(chan int)// 开启goroutine 将 0~100 的数发到 ch1 中go func() {for i := 0; i < 100; i++ {ch1 <- i}close(ch1)}()// 开启goroutine 从ch1中接收值,发送给ch2go func() {for {i, ok := <-ch1if !ok {break}ch2 <- i * i}close(ch2)}()// 在主goroutine 打印ch2for i := range ch2 {fmt.Println("ch2:", i)}
}
- 能从关闭通道中获取值
- 通过遍历获取通道中(关闭的通道也行)的值
8,单向通道
func counter(out chan<- int) {for i := 0; i < 100; i++ {out <- i}close(out)
}func squarer(out chan<- int, in <-chan int) {for i := range in {out <- i * i}close(out)
}
func printer(in <-chan int) {for i := range in {fmt.Println(i)}
}func main() {ch1 := make(chan int)ch2 := make(chan int)go counter(ch1)go squarer(ch2, ch1)printer(ch2)
}
- 1.chan<- int是一个只能发送的通道,可以发送但是不能接收;
- 2.<-chan int是一个只能接收的通道,可以接收但是不能发送。
8,通道总结

相关文章:
go_并发编程(1)
go并发编程 一、 并发介绍1,进程和线程2,并发和并行3,协程和线程4,goroutine 二、 Goroutine1,使用goroutine1)启动单个goroutine2)启动多个goroutine 2,goroutine与线程3࿰…...
第一百一十五回 权限管理包permission_handler
文章目录 概念介绍使用方法示例代码经验分享 我们在上一章回中介绍了局部动态列表相关的内容,本章回中将介绍权限管理包 permission_hanadler.闲话休提,让我们一起Talk Flutter吧。 概念介绍 权限是使用某种功能的授权,比如使用手机上的相机…...
【机器学习】sklearn数据集的使用,数据集的获取和划分
「作者主页」:士别三日wyx 「作者简介」:CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」:对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 sklearn数据集 二、安装sklearn二、获取数据集三、…...
Mysql之 optimizer_trace 相关总结
Mysql之 optimizer_trace 相关总结 MySQL官网介绍:https://dev.mysql.com/doc/dev/mysql-server/latest/PAGE_OPT_TRACE.html 1. 简介 MySQL优化器可以生成Explain执行计划,通过执行计划查看sql是否使用了索引,使用了哪种索; 但…...
【Linux命令详解 | wget命令】 wget命令用于从网络下载文件,支持HTTP、HTTPS和FTP协议
文章标题 简介一,参数列表二,使用介绍1. 基本文件下载2. 递归下载整个网站3. 限制下载速率4. 防止SSL证书校验5. 断点续传6. 指定保存目录7. 自定义保存文件名8. 增量下载9. 使用HTTP代理10. 后台下载 总结 简介 在编程世界中,处理网络资源是…...
DockePod信号处理机制与僵尸进程优化
Docke&Pod信号处理与僵尸进程优化 容器与信号的关系 SIGTERM信号:程序结束(terminate)信号,这是用来终止进程的标准信号,也是 kill 、 killall 、 pkill 命令所发送的默认信号。与SIGKILL不同的是该信号可以被阻塞和处理。通常用来要求程…...
NetApp StorageGRID 对象存储,使您能够跨公有、私有云和混合多云环境管理非结构化数据
NetApp StorageGRID 对象存储,使您能够跨公有、私有云和混合多云环境管理非结构化数据 主要优势 智能:了解行业领先的数据生命周期管理软件。 • 借助 NetApp StorageGRID 基于对象的存储解决方案的数据管理功能、您可以从大型非结构化数据中获得高价值…...
使用Java服务器实现UDP消息的发送和接收(多线程)
目录 简介:1. 导入必要的库2. 创建服务器端代码3. 创建客户端代码4. 实现多线程处理5. 测试运行示例代码:函数说明服务器端代码说明:客户端代码说明: 总结: 简介: 在本篇博客中,我们将介绍如何…...
Linux--查看端口占用情况
查看端口占用情况 在Linux使用过程中,需要了解当前系统开放了哪些端口,并且要查看开放这些端口的具体进程和用户,可以通过netstat命令进行简单查询 netstat命令各个参数说明如下: -t : 指明显示TCP端口 -u : 指明显示UDP…...
微信小程序|自定义弹窗组件
目录 引言小程序的流行和重要性自定义弹出组件作为提升用户体验和界面交互的有效方式什么是自定义弹出组件自定义弹出组件的概念弹出层组件在小程序中的作用和优势为什么需要自定义弹出组件现有的标准弹窗组件的局限性自定义弹出组件在解决这些问题上的优势最佳实践和注意事...
【数据结构】实现顺序表
目录 一.介绍顺序表二.实现顺序表1.创建多文件2.顺序表的存储方式3.函数的声明4.初始化顺序表5.清理顺序表6.打印顺序表7.扩容8.尾插8.尾删9.头插10.头删11.查找12.修改13.在pos位置插入13.在pos位置删除 三.全部代码1.SeqList.h2.SeqList.c3.Test.c 一.介绍顺序表 顺序表是用…...
【嵌入式环境下linux内核及驱动学习笔记-(19)LCD驱动框架2-FrameBuffer】
目录 1、 Frmebuffer(帧缓冲)操作介绍1.1 显示设备的抽象1.2 内存映像1.3 输出画面数据1.4 用户态下操作屏显1.4.1 用文件I / O 操作屏显1.4.2 mmap() 函数1.4.3 ioctl()函数1.4.5 用命令操作屏1.4.6 测试程序 2、Framebuffer总体框架2.1 框架要点2.2 fbmem.c分析2.…...
自己动手写数据库系统:实现一个小型SQL解释器(中)
我们接上节内容继续完成SQL解释器的代码解析工作。下面我们实现对update语句的解析,其语法如下: UpdateCmd -> INSERT | DELETE | MODIFY | CREATE Create -> CreateTable | CreateView | CreateIndex Insert -> INSERT INTO ID LEFT_PARAS Fie…...
HTML 与 XHTML 二者有什么区别
HTML 与 XHTML 二者有什么区别,你觉得应该使用哪一个并说出理由。 HTML 与 XHTML 之间的差别,主要分为功能上的差别和书写习惯的差别两方面。 关于功能上的差别,主要是 XHTML 可兼容各大浏览器、手机以及 PDA,并且浏览器也能快速正…...
fiddler抓包问题记录,支持https、解决 tunnel to 443
fiddler下载安装步骤及基本配置 fiddler抓包教程,如何抓取HTTPS请求,详细教程 可能遇到的问题及解决方案 1. 不能正常访问页面(所有https都无法访问) 解决方案:查看下面配置是否正确 Rules-customization 找到 OnB…...
Kubesphere中DevOps流水线无法部署/部署失败
摘要 总算能让devops运行以后,流水线却卡在了deploy这一步。碰到了两个比较大的问题,一个是无法使用k8sp自带的kubeconfig认证去部署;一个是部署好了以后但是没有办法解析镜像名。 版本信息 k8s:v1.21.5 k8sp:v3.3.…...
使用Nginx解决跨域问题
前言: 项目是公司的老项目,只有部署在服务器上的时候,项目才可以正常运行(接口是通的);现在需求:在现有的项目代码上进行修改,请求接口是第三方给的。接口是正常的,通过A…...
在 OpenCV 中使用深度学习进行年龄检测-附源码
文末附完整源码和模型文件下载链接 在本教程中,我们将了解使用 OpenCV 创建年龄预测器和性别分类器项目的整个过程。 年龄检测 我们的目标是创建一个程序,使用图像来预测人的性别和年龄。但预测年龄可能并不像你想象的那么简单,为什么呢?您可能会认为年龄预测是一个回归问…...
【BASH】回顾与知识点梳理(三十一)
【BASH】回顾与知识点梳理 三十一 三十一. 进程的管理31.1 给进程发送讯号kill -signal PIDlinux系统后台常驻进程killall -signal 指令名称 31.2 关于进程的执行顺序Priority 与 Nice 值nice :新执行的指令即给予新的 nice 值renice :已存在进程的 nice…...
Linux 终端命令之文件浏览(3) less
Linux 文件浏览命令 cat, more, less, head, tail,此五个文件浏览类的命令皆为外部命令。 hannHannYang:~$ which cat /usr/bin/cat hannHannYang:~$ which more /usr/bin/more hannHannYang:~$ which less /usr/bin/less hannHannYang:~$ which head /usr/bin/he…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...
基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...
