从零基础学Go(九)——Go的Goroutine

2024-08-26 06:44
文章标签 基础 go goroutine

本文主要是介绍从零基础学Go(九)——Go的Goroutine,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

简介📃

Go语言(简称Go)是由谷歌公司开发的一种静态强类型、编译型、并发型的编程语言。Go语言的一个显著特点就是对并发编程的良好支持,而Goroutine则是实现这一特性的重要基础。Goroutine可以看作是Go语言中的轻量级线程,它可以帮助开发者轻松实现并发任务,从而更高效地利用多核处理器的能力。

关于计算机中的并发相关知识基础同学们可以参考我的上一期文章 全栈杂谈第一期:什么是计算机中的并发

Goroutine的概念💡

什么是Goroutine?

在Go语言中 是通过 ‘协程’ 来**实现并发**, Goroutine 是 Go 语言特有的名词, 区别于进程 Process, 线程Thread, 协程 Coroutine, 因为 Go语言的作者们觉得是有所区别的,所有专门创造做 Goroutine.

Goroutine 是与其他函数或方法同时运行的函数或方法。 Goroutine 可以被认为是 轻量级的线程,于线程相比 创建 Goroutine 的成本很小,他就是一段代码,一个函数入口 以及在堆上分配的一个堆栈(初始大小为 4k, 会随着程序的执行 自动增长删除)。 因此它非常廉价。GO应用程序可以并发运行着数千个 Goroutine.

Goroutine的启动非常简单,只需要在函数调用前加上 go 关键字即可。这种简便的语法使得并发编程在Go中变得异常简单。

封装main函数的Goroutine 称为 主Goroutine,

主Goroutine 所做的事情并不是执行main函数这么简单,它首先要做的是:假设每一个Goroutine 所能申请的栈控件的最大尺寸,在32位计算机系统中 次尺寸为 256MB,而64位计算机中的尺寸为1GB, **如果有某个 goroutine 的栈空间尺寸大于这个限制,那么运行时系统就会引发一个 栈溢出(stack overflow)**的运行时错误,随后 这个Go 程序也会终止。

此后 主goroutine 会进行一系列的初始化工作, 涉及的工作内容大致如下:

  1. 创建一个特殊的 defer语句,用于在主 goroutine 退出时要做的必要处理。应为主 goroutine 也可能非正常的结束
  2. 创建专用于在后台清扫垃圾内存的 gorontine ,并设置 GC 可用的标识
  3. 执行main 包中的 init 函数
  4. 执行main 函数,执行main 函数之后,它还会检查主 goroutine 是否引发了 运行时错误,并进行必要的处理。最后主 gorontine 才会结束自己以及当前运行的进程

Goroutine与线程的区别

与操作系统级别的线程相比,Goroutine具有以下几个显著的区别:

  • 轻量级:Goroutine在启动时只需要很少的内存(大约几KB),而且随着需要可以动态增长。相比之下,操作系统线程通常需要占用几MB的内存。
  • 调度管理:Goroutine的调度是由Go语言的运行时系统完成的,而不依赖于操作系统的线程调度机制。Go语言的调度器基于协作式调度和抢占式调度结合的方式,能够在不增加复杂性的前提下实现高效的任务切换。
  • 共享内存:Goroutine间共享内存非常方便,这得益于它们运行在同一进程的地址空间内。为了避免共享内存引发的数据竞争问题,Go语言鼓励通过消息传递(Channel)的方式在Goroutine之间进行通信。

Goroutine的生命周期

Goroutine的生命周期可以分为以下几个阶段:

  • 创建:通过 go 关键字启动Goroutine时,Go运行时会为其分配栈空间并创建相应的数据结构。
  • 执行:Goroutine开始执行其对应的函数,Go运行时调度器负责在多个Goroutine之间进行任务切换。
  • 阻塞:Goroutine在遇到I/O操作、Channel通信、锁等情况时可能会被阻塞,此时它会让出CPU,等待条件满足后继续执行。
  • 结束:Goroutine执行完毕或遇到不可恢复的错误时会终止,Go运行时系统回收其栈空间和相关资源。

Goroutine的工作原理🔍️

Go调度器(GMP模型)

Go的调度器基于GMP模型,GMP分别代表Goroutine(G)、线程(M)和调度器(P),它们的关系如下:

  • Goroutine(G):Goroutine是Go语言中的任务单元,每个Goroutine都有自己的栈和上下文信息。
  • 线程(M):M是操作系统中的内核线程,每个M都可以执行一个或多个Goroutine。
  • 调度器(P):P表示逻辑处理器,它管理着与之绑定的一组Goroutine队列。P的数量通常等于可用的CPU核心数量,P负责将Goroutine分配给M执行。

在GMP模型中,Goroutine通过P被分配给M执行。P维护着一个本地的Goroutine队列,当本地队列中的Goroutine不足时,P会尝试从其他P的队列中窃取任务,或从全局队列中获取新的Goroutine。M负责实际执行Goroutine,P则负责Goroutine的调度。

Goroutine的栈管理

在Go语言中,Goroutine是实现并发的核心机制之一。每个Goroutine在创建时都会被分配一个栈,这个栈与操作系统的线程栈不同,它具有动态伸缩的特性。这种设计使得Goroutine在处理并发任务时能够更加高效和灵活。

初始栈大小

Goroutine的栈初始大小非常小,通常只有2KB。这个设计是为了减少内存的初始占用,使得系统可以创建大量的Goroutine而不会对内存造成过大压力。这种小栈的设计也符合Go语言的设计理念,即“以空间换时间”,通过牺牲一定的内存来提高程序的并发性能。

动态扩展

当Goroutine执行过程中需要更多的栈空间时,Go运行时会自动扩展栈的大小。这种扩展是按需进行的,每次扩展的大小通常是以一定的增量进行,这样可以避免频繁的内存分配和释放,提高内存的使用效率。

动态缩减

与动态扩展相对应,当Goroutine不再需要那么多栈空间时,Go运行时也会尝试缩减栈的大小。这种缩减操作可以减少内存的浪费,使得内存资源可以被更有效地利用。

栈管理的优势

  1. 低内存开销:由于初始栈大小小,可以创建大量的Goroutine而不会占用过多的内存。
  2. 灵活性:动态伸缩的栈使得Goroutine可以根据实际需要调整内存使用,适应不同的并发场景。
  3. 高效性:Go运行时的栈管理机制减少了内存分配和释放的开销,提高了程序的运行效率。

栈管理的限制

尽管Go的栈管理机制非常高效,但它也有一些限制。例如,栈的最大大小是有限的,通常由操作系统和Go运行时的配置决定。如果Goroutine的栈增长超过了这个限制,程序可能会因为栈溢出而崩溃。

总的来说,Go语言的Goroutine栈管理机制是一个既高效又灵活的设计,它使得Go语言在并发编程方面具有独特的优势。通过动态地管理栈的大小,Go能够有效地处理大量的并发任务,同时保持较低的内存占用。

Goroutine的基本操作🪡

Goroutine的创建与销毁

Goroutine的创建非常简单,只需要在函数调用前加上 go 关键字。以下是一个简单的示例:

package mainimport ("fmt""time"
)func sayHello() {fmt.Println("Hello, Goroutine!")
}func main() {go sayHello()time.Sleep(time.Second)
}

在这个例子中, sayHello 函数被作为一个Goroutine启动。在主函数结束前,使用 time.Sleep 等待一秒钟,以保证 sayHello 函数有机会执行。Goroutine的销毁是由Go的垃圾回收器自动处理的,当Goroutine执行完毕且没有任何引用时,垃圾回收器会回收其资源。

Goroutine的使用场景🖼️

I/O密集型任务

在处理I/O密集型任务时,如文件读写、网络请求等,Goroutine可以极大地提高程序的并发能力。由于Goroutine在遇到I/O阻塞时会自动让出CPU,其他Goroutine可以利用这些空闲时间执行,从而提升系统的整体吞吐量。

例如,在Web服务器中,处理每个客户端请求时都可以启动一个Goroutine来处理。由于网络I/O通常会有大量的阻塞操作,使用Goroutine可以避免浪费CPU资源。

计算密集型任务

尽管Goroutine在计算密集型任务中的优势不如I/O密集型任务那么明显,但通过合理地将任务分解为多个并行的子任务,Goroutine仍然可以显著提升程序的执行效率。Go语言的 runtime.GOMAXPROCS 函数允许开发者设置并发执行的逻辑CPU数量,从而更好地利用多核处理器。

定时任务和后台任务

Goroutine非常适合用来处理定时任务和后台任务。例如,可以通过 time.Ticker 配合Goroutine实现周期性任务,或者使用Goroutine监控系统状态、处理异步事件等。

常见问题与解决方案❓️

数据竞争

在多个Goroutine并发访问共享数据时,可能会引发数据竞争问题。为了避免数据竞争,可以通过Channel传递数据,或者使用 sync.Mutex 进行加锁。

Goroutine泄漏

如果Goroutine一直处于阻塞状态,或者等待的资源永远无法到达,可能会导致Goroutine泄漏。为了避免Goroutine泄漏,开发者需要仔细设计程序的并发逻辑,确保所有Goroutine能够正常退出。

高效利用CPU资源

Go语言的调度器会自动根据系统的负载调节Goroutine的执行,但在某些情况下,可以通过合理配置 GOMAXPROCS 等参数优化性能,充分利用多核处理器的计算能力。

这篇关于从零基础学Go(九)——Go的Goroutine的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1107844

相关文章

Go标准库常见错误分析和解决办法

《Go标准库常见错误分析和解决办法》Go语言的标准库为开发者提供了丰富且高效的工具,涵盖了从网络编程到文件操作等各个方面,然而,标准库虽好,使用不当却可能适得其反,正所谓工欲善其事,必先利其器,本文将... 目录1. 使用了错误的time.Duration2. time.After导致的内存泄漏3. jsO

go中空接口的具体使用

《go中空接口的具体使用》空接口是一种特殊的接口类型,它不包含任何方法,本文主要介绍了go中空接口的具体使用,具有一定的参考价值,感兴趣的可以了解一下... 目录接口-空接口1. 什么是空接口?2. 如何使用空接口?第一,第二,第三,3. 空接口几个要注意的坑坑1:坑2:坑3:接口-空接口1. 什么是空接

C#基础之委托详解(Delegate)

《C#基础之委托详解(Delegate)》:本文主要介绍C#基础之委托(Delegate),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 委托定义2. 委托实例化3. 多播委托(Multicast Delegates)4. 委托的用途事件处理回调函数LINQ

利用Go语言开发文件操作工具轻松处理所有文件

《利用Go语言开发文件操作工具轻松处理所有文件》在后端开发中,文件操作是一个非常常见但又容易出错的场景,本文小编要向大家介绍一个强大的Go语言文件操作工具库,它能帮你轻松处理各种文件操作场景... 目录为什么需要这个工具?核心功能详解1. 文件/目录存javascript在性检查2. 批量创建目录3. 文件

Go语言中最便捷的http请求包resty的使用详解

《Go语言中最便捷的http请求包resty的使用详解》go语言虽然自身就有net/http包,但是说实话用起来没那么好用,resty包是go语言中一个非常受欢迎的http请求处理包,下面我们一起来学... 目录安装一、一个简单的get二、带查询参数三、设置请求头、body四、设置表单数据五、处理响应六、超

Golang基于内存的键值存储缓存库go-cache

《Golang基于内存的键值存储缓存库go-cache》go-cache是一个内存中的key:valuestore/cache库,适用于单机应用程序,本文主要介绍了Golang基于内存的键值存储缓存库... 目录文档安装方法示例1示例2使用注意点优点缺点go-cache 和 Redis 缓存对比1)功能特性

Go 1.23中Timer无buffer的实现方式详解

《Go1.23中Timer无buffer的实现方式详解》在Go1.23中,Timer的实现通常是通过time包提供的time.Timer类型来实现的,本文主要介绍了Go1.23中Timer无buff... 目录Timer 的基本实现无缓冲区的实现自定义无缓冲 Timer 实现更复杂的 Timer 实现总结在

Go使用pprof进行CPU,内存和阻塞情况分析

《Go使用pprof进行CPU,内存和阻塞情况分析》Go语言提供了强大的pprof工具,用于分析CPU、内存、Goroutine阻塞等性能问题,帮助开发者优化程序,提高运行效率,下面我们就来深入了解下... 目录1. pprof 介绍2. 快速上手:启用 pprof3. CPU Profiling:分析 C

使用Go语言开发一个命令行文件管理工具

《使用Go语言开发一个命令行文件管理工具》这篇文章主要为大家详细介绍了如何使用Go语言开发一款命令行文件管理工具,支持批量重命名,删除,创建,移动文件,需要的小伙伴可以了解下... 目录一、工具功能一览二、核心代码解析1. 主程序结构2. 批量重命名3. 批量删除4. 创建文件/目录5. 批量移动三、如何安

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应