go基础知识归纳总结

news/2024/9/18 3:12:36 标签: golang, 开发语言, 后端

无缓冲的 channel 和有缓冲的 channel 的区别?

在 Go 语言中,channel 是用来在 goroutines 之间传递数据的主要机制。它们有两种类型:无缓冲的 channel 和有缓冲的 channel。

  1. 无缓冲的 channel

行为:无缓冲的 channel 是一种同步的通信方式,发送和接收必须同时发生。如果一个 goroutine 试图通过无缓冲 channel 发送数据,它会阻塞,直到另一个 goroutine 从该 channel 中接收数据。反之亦然,接收方在准备好接收数据之前,发送方无法继续执行。
用法:适合在两个 goroutines 之间实现精确的同步,确保它们在同一时刻传递数据。
优点:
保证了发送和接收的同步,可以避免某些类型的并发错误。
更简单,适合需要严格按顺序处理任务的场景。
缺点:
性能上可能存在瓶颈,因为必须等待对应的发送或接收操作才能继续执行。

无缓冲的 Channel 示例

package main

import (
	"fmt"
	"time"
)

func main() {
	// 创建一个无缓冲的 channel
	ch := make(chan int)

	// 启动一个 goroutine 来接收数据
	go func() {
		// 接收数据之前会阻塞,直到 main goroutine 发送数据
		val := <-ch
		fmt.Println("接收到的数据:", val)
	}()

	// 模拟一些操作
	time.Sleep(1 * time.Second)

	// 发送数据到 channel,会阻塞直到接收方读取数据
	ch <- 42

	fmt.Println("数据已发送")
}

由于是无缓冲的 channel,main goroutine 在发送 42 时会阻塞,直到 goroutine 从 channel 中接收到这个值,程序才会继续执行。
2. 有缓冲的 channel

行为:有缓冲的 channel 容许在 channel 中存储一定数量的数据元素,发送方可以在 channel 未满时继续发送数据,而无需等待接收方。接收方只有当 channel 非空时才会接收数据。
用法:适合发送方和接收方的处理速度不一致的情况,允许发送方先发送一部分数据,接收方稍后接收。
优点:
提供了一定的并发灵活性,发送方可以在接收方未准备好时先发送一定数量的数据。
提高了性能,减少了因为同步阻塞而导致的性能损耗。
缺点:
如果缓冲区设计不当,可能会出现缓冲区溢出或浪费资源的情况。
由于有缓冲的存在,可能会导致发送和接收之间的时间不同步,增加调试和排查问题的难度。
总结
无缓冲的 channel 强调的是同步性,适合需要严格同步的场景。

有缓冲的 channel 提供更多的灵活性,允许在并发处理中有更大的自由度,不需要完全同步。
在使用中,可以根据具体场景选择合适的 channel 类型。例如,在生产者-消费者模型中,有缓冲的 channel 可以防止生产者等待消费者处理;而在需要精确同步的任务中,无缓冲 channel 则更加合适。

有缓冲的 Channel 示例

package main

import (
	"fmt"
	"time"
)

func main() {
	// 创建一个带有缓冲区大小为 2 的 channel
	ch := make(chan int, 2)

	// 发送两个数据到 channel
	ch <- 1
	fmt.Println("发送了数据 1")
	ch <- 2
	fmt.Println("发送了数据 2")

	// 此时,由于缓冲区还有空间,发送不会阻塞
	go func() {
		// 延迟读取,模拟一些操作
		time.Sleep(2 * time.Second)
		val := <-ch
		fmt.Println("接收到的数据:", val)
	}()

	// 继续发送数据
	time.Sleep(1 * time.Second)
	ch <- 3
	fmt.Println("发送了数据 3")

	// 接收数据
	time.Sleep(2 * time.Second)
	val := <-ch
	fmt.Println("接收到的数据:", val)
}

这里的 channel 有缓冲区大小为 2,因此前两个 ch <- 操作不会阻塞,因为缓冲区有足够空间。第三次发送数据时,如果缓冲区已满,发送方会阻塞,直到接收方读取数据并释放空间。

channel和select底层数据结构是怎样的?

Go 中 select 语句的底层实现涉及多个关键数据结构和调度机制,主要是为了高效地处理通道(channel)操作和 Goroutine 调度。我们可以从 Go 语言的源代码中窥探其底层数据结构。以下是 select 相关的几个重要底层数据结构和其如何与通道和 Goroutine 协同工作:

  1. Goroutine 和 P 结构
    Go 的并发模型基于 Goroutine 和 M
    调度模型。每个 select 语句本质上都会涉及到 Goroutine 的阻塞与唤醒。调度器的核心数据结构包括:
    Goroutine(G):代表一个执行中的协程,每个 Goroutine 都包含了当前执行状态、栈信息等。当某个 select 语句阻塞 Goroutine 时,Goroutine 会被挂起,并与通道关联。
    P(Processor):代表一个运行 Goroutine 的处理器,它与 OS 线程(M)配合使用,管理并调度多个 Goroutine。
    当某个 select 语句涉及到通道的操作时,如果通道未就绪,当前 Goroutine 会被放入通道的等待队列中,并挂起,直到被调度器唤醒。
  1. 通道(Channel)结构
    通道的底层结构非常重要,因为 select 语句的核心在于处理通道操作。通道的内部结构如下
type hchan struct {
    qcount   uint           // 通道中已经存在的数据个数
    dataqsiz uint           // 环形队列的大小
    buf      unsafe.Pointer // 环形队列的指针
    elemsize uint16         // 每个元素的大小
    closed   uint32         // 通道是否关闭
    sendx    uint           // 发送操作的索引
    recvx    uint           // 接收操作的索引
    recvq    waitq          // 等待接收的 Goroutine 队列
    sendq    waitq          // 等待发送的 Goroutine 队列
}

recvq/sendq:表示接收和发送操作等待的 Goroutine 队列。当 select 语句中有对通道的接收或发送操作时,如果通道未就绪,当前 Goroutine 会被加入相应的等待队列。

  1. SelectCase 结构
    Go 运行时使用一个名为 SelectCase 的数据结构来表示 select 语句中的每个 case,每个 SelectCase 代表一个通道操作。该结构体中记录了每个 case 中的通道、操作类型(发送或接收)以及相关的数据指针等。
type scase struct {
    c    *hchan         // 指向通道的指针
    kind uint16         // 操作类型(发送、接收)
    pc   uintptr        // 程序计数器,用于跟踪执行位置
    elem unsafe.Pointer // 数据元素的指针,用于发送或接收操作
}

c:指向通道的指针,表示这个 case 监听哪个通道。
kind:表示操作类型,是发送、接收还是默认 case。
elem:存储数据的指针,用于发送或接收操作时的存取。

  1. Select 语句的执行流程
    当 Goroutine 执行一个 select 语句时,Go 运行时会执行以下操作:
    初始化 scase 列表:首先,select 语句会初始化每个通道操作,生成一个 scase 列表来表示所有的 case。
    检测是否有就绪通道:然后,运行时会遍历这些 scase,检测是否有通道已经就绪(比如是否有数据可接收,或者通道是否可以发送数据)。如果有通道就绪,立刻执行相应的操作,并返回。
    阻塞等待:如果所有通道都未就绪,当前 Goroutine 会挂起并加入到每个通道的等待队列中。此时,通道内部的 recvq 或 sendq 队列会保存当前 Goroutine 的相关信息,当通道状态发生变化时,这些队列会被唤醒,调度器会重新调度等待的 Goroutine 。
    随机选择通道:当有多个通道同时就绪时,Go 运行时通过随机函数来选择一个通道执行,保证公平性。
  2. 调度器与 select
    Go 调度器通过一组全局的队列和局部队列来管理 Goroutine 的运行状态。在 select 语句中,阻塞的 Goroutine 会被挂起到通道的等待队列中,但它们仍然保留在全局或局部队列中。当通道状态发生变化(如通道中有数据),调度器会从队列中唤醒相关的 Goroutine 并将其重新加入执行队列。
  3. select 的公平性和随机性
    Go 在 select 中实现了对多个通道操作的随机选择机制,避免某些通道操作被长期饿死。具体来说,当有多个通道同时就绪时,Go 会打乱 scase 列表的顺序,并随机选择一个通道进行处理。这确保了 select 的公平性,即使多个 Goroutine 同时监听同一组通道,也不会导致某个通道长期得不到处理。

select 的核心数据结构总结
Goroutine (G):Goroutine 是 select 语句中的执行单元,当一个 select 阻塞时,当前 Goroutine 会挂起。
hchan:通道是核心数据结构,负责管理发送和接收操作,recvq 和 sendq 队列保存了等待通道操作的 Goroutine。
scase:select 语句中每个通道操作的表示,存储了通道的指针、操作类型等信息。
调度器:Go 调度器负责管理 Goroutine 的执行和状态,当 select 语句涉及到阻塞操作时,调度器会将 Goroutine 挂起并重新调度。
通过这些底层机制,Go 的 select 语句能够高效地在并发场景下处理多个通道操作,并且在多个通道就绪时提供随机选择的公平性保障。


http://www.niftyadmin.cn/n/5653307.html

相关文章

卷轴模式系统源码开发:探索游戏世界——游戏模式的设计

在电子游戏的发展历程中&#xff0c;卷轴模式&#xff08;Scrolling Mode&#xff09;作为一种经典且广泛应用的游戏界面呈现方式&#xff0c;为玩家提供了沉浸式的探索体验。从早期的《超级马里奥兄弟renxb001》到现代的《塞尔达传说》系列&#xff0c;卷轴模式不仅定义了众多…

MIG控制器破解(1):phy_control_001.vp破解(verilog)

完整代码压缩包会在最后一章节上传。 DDR控制器MIG底层硬件逻辑: phy_control_001文件模块解码: `timescale 1ps / 1ps module sip_phy_control ( input [3:0] ao_toggle, input [3:0] ao_wrlvl_en, input burst_mode, input [2:0] clk_ratio, input [5:0] cmd_offset, inp…

有关 Element-ui 的一些思考

本篇文章是基于 element-ui 的 主题样式修改 曾经为了修改组件风格,一个项目用了上百个样式穿透,后来才发现定制一个主题就够了! 第一步,在官网的主题页面,修改背景色、字体颜色及边框颜色 第二步,下载主题 第三步,用下载的css文件替换掉默认的css文件

【机器学习-监督学习】集成学习与梯度提升决策树

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈Python机器学习 ⌋ ⌋ ⌋ 机器学习是一门人工智能的分支学科&#xff0c;通过算法和模型让计算机从数据中学习&#xff0c;进行模型训练和优化&#xff0c;做出预测、分类和决策支持。Python成为机器学习的首选语言&#xff0c;…

数据结构之栈和队列的应用

目录 一、栈的应用 1. 括号匹配 2. 计算后缀表达式 方法一&#xff08;栈&#xff09; 方法二&#xff08;数组模拟栈&#xff09; 二、队列应用 1. 二叉树层序遍历 方法一&#xff08;队列&#xff09; 三、总结 一、栈的应用 1. 括号匹配 给定一个只包括 (&#xf…

WXpython --- python桌面应用开发

WXPython 教程&#xff1a;从零开始构建GUI应用程序 WXPython 是一个用于Python的跨平台GUI工具包&#xff0c;它允许Python开发者创建具有本地外观和感觉的桌面应用程序。在本教程中&#xff0c;我们将从安装WXPython开始&#xff0c;逐步学习如何使用它来创建一个简单的GUI应…

树莓派3B点灯(5)-- 自写驱动(按键版)(TODO)

在树莓派上新增一个按键并通过内核模块&#xff08;ko 驱动&#xff09;来处理&#xff0c;可以通过编写一个 Linux 内核模块来实现。以下是一个简单的示例&#xff0c;展示如何编写和加载一个内核模块来处理 GPIO 按键输入。 硬件连接 假设你已经将按键连接到树莓派的 GPIO 17…

语义分割数据集|河流湖泊分割|水灾预警

江河湖泊自然水灾检测数据集&#xff0c;数据集整理不易&#xff0c;获取地址在最后&#xff0c;具体信息如下&#xff1a; 总数&#xff1a;290张 类别&#xff1a;1类 数据集大小&#xff1a;约106M 数据整理不易&#xff0c;数据集获取地址如下&#xff1a; https://…