【Golang 面试题】每日 3 题(二十三)

✍个人博客:Pandaconda-CSDN博客

📣专栏地址:http://t.csdnimg.cn/UWz06

📚专栏简介:在这个专栏中,我将会分享 Golang 面试中常见的面试题给大家~

❤️如果有收获的话,欢迎点赞👍收藏📁,您的支持就是我创作的最大动力💪

67. Go channel 如何发送?

发送操作,编译时转换为 runtime.chansend 函数。

go 复制代码
func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool 

阻塞式:

调用 chansend 函数,并且 block = true。

go 复制代码
ch <- 10

非阻塞式:

调用 chansend 函数,并且 block = false。

go 复制代码
select {
    case ch <- 10:
    ...
  default
}

向 channel 中发送数据时大概分为两大块:检查和数据发送,数据发送流程如下:

  • 如果 channel 的读等待队列存在接收者 goroutine
    • 将数据直接发送给第一个等待的 goroutine, 唤醒接收的 goroutine。
  • 如果 channel 的读等待队列不存在接收者 goroutine
    • 如果循环数组 buf 未满,那么将会把数据发送到循环数组 buf 的队尾。
    • 如果循环数组 buf 已满,这个时候就会走阻塞发送的流程,将当前 goroutine 加入写等待队列,并挂起等待唤醒。

68. Go channel 如何接收和关闭?

发送操作,编译时转换为runtime.chanrecv函数。

go 复制代码
func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) 

阻塞式:

调用 chanrecv 函数,并且 block = true。

go 复制代码
<ch
v := <ch
v, ok := <ch
// 当channel关闭时,for循环会自动退出,无需主动监测channel是否关闭,可以防止读取已经关闭的channel,造成读到数据为通道所存储的数据类型的零值
for i := range ch {
    fmt.Println(i)
}

非阻塞式:

调用 chanrecv 函数,并且 block = false。

go 复制代码
select {
    case <-ch:
    ...
  default
}

向 channel 中接收数据时大概分为两大块,检查和数据发送,而数据接收流程如下:

  • 如果 channel 的写等待队列存在发送者 goroutine
    • 如果是无缓冲 channel,直接从第一个发送者 goroutine 那里把数据拷贝给接收变量,唤醒发送的 goroutine。
    • 如果是有缓冲 channel(已满),将循环数组 buf 的队首元素拷贝给接收变量,将第一个发送者 goroutine 的数据拷贝到 buf 循环数组队尾,唤醒发送的 goroutine。
  • 如果 channel 的写等待队列不存在发送者 goroutine
    • 如果循环数组 buf 非空,将循环数组 buf 的队首元素拷贝给接收变量。
    • 如果循环数组 buf 为空,这个时候就会走阻塞接收的流程,将当前 goroutine 加入读等待队列,并挂起等待唤醒。

关闭

关闭操作,调用 close 函数,编译时转换为 runtime.closechan 函数。

go 复制代码
close(ch)
func closechan(c *hchan) 

69. Go channel 案例分析

go 复制代码
package main
import (
    "fmt"
    "time"
    "unsafe"
)
func main() {
  // ch是长度为4的带缓冲的channel
  // 初始hchan结构体重的buf为空,sendx和recvx均为0
    ch := make(chan string, 4)
    fmt.Println(ch, unsafe.Sizeof(ch))
    go sendTask(ch)
    go receiveTask(ch)
    time.Sleep(1 * time.Second)
}
// G1是发送者
// 当G1向ch里发送数据时,首先会对buf加锁,然后将task存储的数据copy到buf中,然后sendx++,然后释放对buf的锁
func sendTask(ch chan string) {
    taskList := []string{"this", "is", "a", "demo"}
    for _, task := range taskList {
        ch <- task //发送任务到channel
    }
}
// G2是接收者
// 当G2消费ch的时候,会首先对buf加锁,然后将buf中的数据copy到task变量对应的内存里,然后recvx++,并释放锁
func receiveTask(ch chan string) {
    for {
        task := <-ch                  //接收任务
        fmt.Println("received", task) //处理任务
    }
}

总结 hchan 结构体的主要组成部分有四个:

  • 用来保存 goroutine 之间传递数据的循环数组:buf
  • 用来记录此循环数组当前发送或接收数据的下标值:sendx 和 recvx
  • 用于保存向该 chan 发送和从该 chan 接收数据被阻塞的 goroutine 队列: sendq 和 recvq
  • 保证 channel 写入和读取数据时线程安全的锁:lock
相关推荐
小小码农Come on4 分钟前
QT中窗口位置、相对位置、绝对位置
android·开发语言·qt
diediedei6 分钟前
C++中的适配器模式变体
开发语言·c++·算法
郝学胜-神的一滴15 分钟前
Python中的Mixin继承:灵活组合功能的强大模式
开发语言·python·程序人生
June bug15 分钟前
(#数组/链表操作)最长上升子序列的长度
数据结构·程序人生·leetcode·链表·面试·职场和发展·跳槽
叫我:松哥16 分钟前
基于python强化学习的自主迷宫求解,集成迷宫生成、智能体训练、模型评估等
开发语言·人工智能·python·机器学习·pygame
晚霞的不甘16 分钟前
Flutter for OpenHarmony 创意实战:打造一款炫酷的“太空舱”倒计时应用
开发语言·前端·flutter·正则表达式·前端框架·postman
WKP941820 分钟前
线程并行控制CompletableFuture
java·开发语言
飞机和胖和黄26 分钟前
考研之C语言第二周作业
c语言·开发语言·考研
输出输入32 分钟前
MT4 EA 设计一次一单方法
开发语言
一起养小猫35 分钟前
OpenHarmony 实战中的 Flutter:深入理解 Widget 核心概念与底层原理
开发语言·flutter