惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

大猫的无限游戏
大猫的无限游戏
月光博客
月光博客
博客园 - Franky
博客园 - 三生石上(FineUI控件)
爱范儿
爱范儿
博客园 - 司徒正美
博客园 - 叶小钗
Apple Machine Learning Research
Apple Machine Learning Research
美团技术团队
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Cloudflare Blog
B
Blog RSS Feed
阮一峰的网络日志
阮一峰的网络日志
宝玉的分享
宝玉的分享
V
Visual Studio Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
IT之家
IT之家
博客园_首页
S
SegmentFault 最新的问题
A
About on SuperTechFans
Blog — PlanetScale
Blog — PlanetScale
GbyAI
GbyAI
H
Help Net Security
MongoDB | Blog
MongoDB | Blog

秋码分享

我是如何解决将 c++ 编译成可以在 node.js 中使用的 *.node,中间出现的一大堆问题的(指纹浏览器基石篇) eSIM Plus 爱沙尼亚手机号彻底翻车?“永久有效”悄然变成了一年! 接码平台 SMS-Activate 余额可以转移到新平台使用,截止日期:2026年1月29日 是时候将 hugo-theme-kiwi 主题提交到 themes.gohugo.io 站点上了 Flux2 刚开源就凉了?Z-Image 本地部署狠狠打了个样 声音的未来:Chatterbox —— 用「夸张度旋钮」提升表现力的开源 TTS 向导 还以为那只是换个背景?Qwen-Image-Edit 在 ComfyUI 中能做到更离谱的事 Windows 结合最新版 ComfyUI 部署阿里最新开源的 Qwen-Image 图像大模型 从零样本到跨场景:Seed-VC语音转换技术的革命性突破 大语音模型轻量化革命:MegaTTS3 如何重新定义文本生成语音的技术边界(windows篇) 竞赛级编程大模型OlympicCoder-7B之本地部署(Windows篇) 阿里开源了端到端全模态大模型Qwen-2.5-Omini-7B之本地部署(windows篇) 语音识别之whisper本地部署(实时语音之开篇) 甭管是个人还是企业都能部署的Mistral-Small3.1,远超同级别的模型 文生音乐开源项目DiffRhythm,8G显存本地部署之Windows篇 阿里QwQ-32B本地部署指南:用Ollama轻松运行320亿参数大模型 基于Qwen2.5大模型的Spark-TTS,零样本语音克隆,CPU可运行之本地部署(Windows篇) 智谱开源了文生图CogView4-6B模型,支持中文提示词之本地部署(Windows篇) 基于歌词生成整首歌的开源AI音乐模型,支持中、英、日、韩等多种语言,本地化部署YuE(windows篇) 阿里云开源的文生视频万相 Wan2.1之本地部署Wan2.1-T2V-1.3B模型 互动式开源AI图像编辑神器,Windows11本地部署 MagicQuill 本地部署Qwen2.5-VL-7B-Instruct多模态视觉大模型(Windows篇) 保持角色一致性的绘本生成AI开源项目之Story-Adapter本地部署Windows篇 本地部署 Stable Diffusion 3.5(最新 ComfyUI记录篇) 谁说Win7安装不了Node.js最新版的呢?都2025年,还不更新系统到Win11 vs code远程调试Linux服务器上的php代码 浏览器定制 | Windows11 编译 Chromium 133.0.6885.0(截稿前Chromium最新版之编译篇[一]) 不说是彻底搞懂,至少让你不再惧怕c/c++指针,以及各种奇葩指针变种 解决windows下php8.x及以上版本,在Apache2.4中无法加载CURL扩展的问题 在 Windows8.1 下编译 Chromium (103.0.5060.68 之三)
Go 语言中的带有缓冲 Channel(Let's Go 三十一)
一个游离于山间之上的江湖漫行者。A jungle wanderer who wanders above the mountain · 2022-10-31 · via 秋码分享

无缓冲信道 Channel 是无法保存任何值的,该类型信道要求 发送 goroutine 和 接受 goroutine 两者同时准备好,这样才能完成发送与接受的操作。

1、无缓冲 Channel

前几篇文章我们使用 make 创建出来的信道 Channel,并没有传入第二个参数,也就是说,没有传入第二个参数创建的信道 Channel,便是无缓冲信道咯。

无缓冲信道 Channel 是无法保存任何值的,该类型信道要求 发送 goroutine 和 接受 goroutine 两者同时准备好,这样才能完成发送与接受的操作。

假使 两者 goroutine 未能同时准备好,信道便会先执行 发送 和 接受 的操作, goroutine 会阻塞等待。这种对信道进行发送和接收的交互行为本身就是同步的。其中任意一个操作都无法离开另一个操作单独存在。

阻塞指的是由于某种原因数据没有到达,当前协程(线程)持续处于等待状态,直到条件满足才解除阻塞。

同步指的是在两个或多个协程(线程)之间,保持数据内容一致性的机制。

package main

import (
    "fmt"
    "math/rand"
    "sync"
    "time"
)

// wg 用来等待程序结束
var wg sync.WaitGroup

func init() {
    rand.Seed(time.Now().UnixNano())
}

func main() {
    // 创建一个无缓冲的通道
    court := make(chan int)
    // 计数加 2,表示要等待两个goroutine
    wg.Add(2)
    // 启动两个选手
    go player("Nadal", court)
    go player("Djokovic", court)
    // 发球
    court <- 1
    // 等待游戏结束
    wg.Wait()
}

// player 模拟一个选手在打网球
func player(name string, court chan int) {
    // 在函数退出时调用Done 来通知main 函数工作已经完成
    defer wg.Done()
    for {
        // 等待球被击打过来
        ball, ok := <-court
        if !ok {
            // 如果通道被关闭,我们就赢了
            fmt.Printf("Player %s Won\n", name)
            return
        }
        // 选随机数,然后用这个数来判断我们是否丢球
        n := rand.Intn(100)
        if n%13 == 0 {
            fmt.Printf("Player %s Missed\n", name)
            // 关闭通道,表示我们输了
            close(court)
            return
        }
        // 显示击球数,并将击球数加1
        fmt.Printf("Player %s Hit %d\n", name, ball)
        ball++
        // 将球打向对手
        court <- ball
    }
}

img

2、有缓冲 Channel

有缓冲的 Channel )是一种在被接收前能存储一个或者多个值。这种类型的信道并不强制要求 goroutine 之间必须同时完成发送和接收。信道会阻塞发送和接收动作的条件也会不同。只有在信道中没有要接收的值时,接收动作才会阻塞。只有在信道没有可用缓冲区容纳被发送的值时,发送动作才会阻塞。

这导致有缓冲的信道和无缓冲的信道之间的一个很大的不同:无缓冲的信道保证进行发送和接收的 goroutine 会在同一时间进行数据交换;有缓冲的信道没有这种保证。

在无缓冲通道的基础上,为信道增加一个有限大小的存储空间形成带缓冲信道。带缓冲信道在发送时无需等待接收方接收即可完成发送过程,并且不会发生阻塞,只有当存储空间满时才会发生阻塞。同理,如果缓冲通道中有数据,接收时将不会发生阻塞,直到信道中没有数据可读时,信道将会再度阻塞。

varChan := make(chan chanType,chanSize)
//chanSize 决定可以保存多少大小的元素数量
package main

import (
    "fmt"
)

func main() {
    // 创建一个4个元素缓冲大小的 string 通道
    ch := make(chan string, 4)
    // 查看当前通道的大小
    fmt.Println(len(ch))
    // 发送4个 string 元素到通道
    ch <- "秋码记录"
    ch <- "https://qiucode.cn"
    ch <- "你我杂志刊"
    ch <- "仗剑行于江湖"
    // 查看当前通道的大小
    fmt.Println(len(ch))
}

img

带缓冲信道在很多特性上和无缓冲信道是类似的。无缓冲信道可以看作是长度永远为 0 的带缓冲信道。因此根据这个特性,带缓冲信道在下面列举的情况下依然会发生阻塞:

  • 带缓冲信道被填满时,尝试再次发送数据时发生阻塞。
  • 带缓冲信道为空时,尝试接收数据时发生阻塞。

为什么Go语言对信道要限制长度而不提供无限长度的信道?

我们知道信道(channel)是在两个 goroutine 间通信的桥梁。使用 goroutine 的代码必然有一方提供数据,一方消费数据。当提供数据一方的数据供给速度大于消费方的数据处理速度时,如果信道不限制长度,那么内存将不断膨胀直到应用崩溃。因此,限制信道的长度有利于约束数据提供方的供给速度,供给数据量必须在消费方处理量+信道长度的范围内,才能正常地处理数据。