Go语言学习笔记 --- concurrency、channel、select

Go语言的 的并发编程(Concurrency)核心知识 并发编程是一种计算模型,允许多个计算过程同时进行。Go语言(Golang)是由Google开发的一种编程语言,它以简洁、高效和内置并发支持而著称。这使得Go在处理网络服务、云计算和其他需要高并发的场景中,非常受欢迎。在这篇文章中,我们将深入探讨Go语言的并发编程核心知识,包括Go的并发模型、goroutine、channel、并发控制工具等。 阅读详情

学习笔记根据 无闻 go语言基础教程 整理

concurrency

  • 很多人都是冲着 Go 大肆宣扬的高并发而忍不住跃跃欲试,但其实从源码的解析来看,goroutine 只是由官方实现的超级”线程池”。
  • 不过话说回来,每个实例 4-5KB 的栈内存占用和由于实现机制而大幅减少的创建和销毁开销,是制造 Go 号称的高并发的根本原因。
  • 另外,goroutine 的简单易用,也在语言层面上给予了开发者巨大的便利。
  • 并发不是并行:Concurrency Is Not Parallelism
  • 并发主要由切换时间片来实现“同时”运行,在并行则是直接利用
  • 多核实现多线程的运行,但 Go 可以设置使用核数,以发挥多核计算机的能力。
  • Goroutine 奉行通过通信来共享内存,而不是共享内存来通信。

channel

  • Channel 是 goroutine 沟通的桥梁,大都是阻塞同步的
  • 通过 make 创建,close 关闭
  • Channel 是引用类型
  • 可以使用 for range 来迭代不断操作 channel
  • 可以设置单向或双向通道
  • 可以设置缓存大小,在未被填满前不会发生阻塞

select

  • 可处理一个或多个 channel 的发送与接收
  • 同时有多个可用的 channel时按随机顺序处理
  • 可用空的 select 来阻塞 main 函数
  • 可设置超时

示例代码

package main

import (
    "fmt"
    "time"
    "runtime"
    "sync"
)


func main() {
    // 说明,由于存在异步和阻塞等情况,测试时候需要一个函数一个函数的执行
    // 执行一个函数时,注释掉其他测试函数
    test1T()
    // test2()
    // test3()
    // test4()
    // test5()
    // test6()
    // test7T()
    // test8T()
    // test9T()
    // test10T()
    // test11T()
    // test12()
    // test13()
    // test14()
}

func test1() {
    fmt.Println("Go Go Go !!!")
}

func test1T() {
    fmt.Println("=============test1T=============")
    go test1() // 先启动goroutine
    time.Sleep(2 * time.Second) // 程序等待2s后退出
}

// 使用channel 来进行消息的发送
func test2() {
    fmt.Println("=============test2=============")
    c := make(chan bool) // 创建一个channel
    // 下面是用匿名函数
    go func() {
        fmt.Println("Go2") // Go2
        c <- true // c 存进一个bool型
    }()
    <-c // 在外面取出来 函数在执行到这一步的时候是阻塞状态,等c 存完之后才会执行这里,结束运行
}

// 可以使用 for range 来迭代不断操作 channel,等待channel完成,利用close函数,最后退出程序
func test3() {
    fmt.Println("=============test3=============")
    c := make(chan bool) // 创建一个channel ,make 即能存,又能取 (属于双向通道)
    // 下面是用匿名函数
    go func() {
        fmt.Println("Go2")
        c <- true // c 存进一个bool型
        close(c) // 在迭代时一定要注意close,否则会死锁,最终崩溃退出。
    }()
    for v := range c {
        fmt.Println(v) // true
    }
}

// 测试 使用无缓存的channel
func test4() {
    fmt.Println("=============test4=============")
    c := make(chan bool)
    go func() {
        fmt.Println("Go3")
        c <- true
    }()
    <- c // main 函数先执行,再去执行 goroutine, 此处是一直等待状态, 直到取出东西来,main函数退出
}

// 测试 使用无缓存的channel, 尝试位置调换, 无缓存是同步阻塞状态,直到读取到,才会结束
func test5() {
    fmt.Println("=============test5=============")
    c := make(chan bool)
    go func() {
        fmt.Println("Go3") // Go3
        <- c
    }()
    c <- true
}

// 测试使用有缓存的channel, 并且对调位置 ,有缓存是异步的
func test6() {
    fmt.Println("=============test6=============")
    c := make(chan bool, 1)
    go func() {
        fmt.Println("Go3") // 未输出
        <- c

    }()
     c <- true // 直接退出
}

func test7() {
    a := 1
    for i := 0; i<10000000; i++ {
        a += i
    }
    fmt.Println(a)
}

// 循环10个 goroutine ,什么都没输出
func test7T() {
    fmt.Println("=============test7T=============")
    for i := 0; i < 10; i++ {
        go test7()
    }
}

func test8(c chan bool, index int) {
    a := 1
    for i := 0; i<100; i++ {
        a += i
    }
    fmt.Println(index, a)
    if index == 9 {
        c <- true
    }
}

// 循环输出10个 goroutine , 但结果并未按照想象中的,10个分别输出执行,可能是go的api版本升级导致的, 默认使用多核处理
// 意想不到的结果是 每个goroutine 都是独立的,
// 如果第9个先输出,那么其他的可能并不会输出而是直接结束
func test8T() {
    fmt.Println("=============test8T=============")
    c := make(chan bool)
    for i := 0; i < 10; i++ {
        go test8(c, i)
    }
    <-c
}

// 循环10个 goroutine , 但是每个goroutine 都是独立的,如果第9个先输出,那么其他的可能并不会输出而是直接结束
// 同样使用test8 , 现在的情况是,输出结果和test8T是一样的
func test9T() {
    fmt.Println("=============test9T=============")
    runtime.GOMAXPROCS(runtime.NumCPU()) // 使用多核CPU,分配不定的, 不能够用最后一个goroutine的结果,作为10个都完成的判定
    c := make(chan bool)
    for i := 0; i < 10; i++ {
        go test8(c, i)
    }
    <-c
}

func test10(c chan bool, index int) {
    a := 1
    for i := 0; i<100; i++ {
        a += i
    }
    fmt.Println(index, a)
    c <- true
}

// 设置一个缓存长度为10的channel, 使用此种方法,10个channel会分别输出执行
func test10T() {
    fmt.Println("=============test10T=============")
    runtime.GOMAXPROCS(runtime.NumCPU()) // 使用多核CPU,分配不定的, 不能够用最后一个goroutine的结果,作为10个都完成的判定
    c := make(chan bool, 10) // 此处channel 长度为10
    for i := 0; i < 10; i++ {
        go test10(c, i)
    }
    // 循环取10次
    for i := 0; i < 10; i ++ {
        <-c
    }
}

// 使用第二种方法来完成执行10个channel
// 通过sync 同步包中的waitgroup 来创建一个任务组,在任务组中添加要完成的任务数
// 在没完成一次任务,就标记一次档,待完成任务数就会往下减,直到为0时,全部任务就会完成。

func test11(wg *sync.WaitGroup, index int) {
    a := 1
    for i := 0; i < 100; i++ {
        a += i
    }
    fmt.Println(index, a)

    wg.Done()
}

func test11T() {
    fmt.Println("=============test11T=============")
    runtime.GOMAXPROCS(runtime.NumCPU()) // 使用多核CPU,分配不定的, 不能够用最后一个goroutine的结果,作为10个都完成的判定
    wg := sync.WaitGroup{}
    wg.Add(10)
    for i := 0; i < 10; i++ {
        go test11(&wg, i)
    }
    wg.Wait()
}

// 使用select 测试多个channel
func test12() {
    fmt.Println("=============test12=============")
    c1, c2 := make(chan int), make(chan string)
    o := make(chan bool, 2) // 设置一个缓存为2的channel
    go func() {
        for {
            select {
            case v, ok := <-c1:
                if !ok {
                    o <- true
                    break
                }
                fmt.Println("c1",v)
            case v, ok := <- c2:
                if !ok {
                    o <- true
                    break
                }
                fmt.Println("c2", v)
            }
        }
    }()

    c1 <- 1
    c2 <- "hi"
    c1 <- 3
    c2 <- "hello"

    close(c1)
    close(c2)

    for i := 0; i < 2; i ++ {
        <- o
    }

    /*
    // 程序运行结果:
    c1 1
    c2 hi
    c1 3
    c2 hello

     */
}

func test13() {
    fmt.Println("=============test13=============")
    c := make(chan int)
    go func() {
        // 不断读出c的值
        for v := range c {
            fmt.Println(v)
        }
    }()

    // 随机向c中写入0或者1
    for i := 0; i < 6; i++ {
        select {
        case c <- 0:
        case c <- 1:
        }
    }

    /*
    // 随机输出结果:
    1
    1
    0
    0
    0

     */
}

// select 可设置超时
func test14() {
    fmt.Println("=============test14=============")
    c := make(chan bool)
    select {
    case v:= <-c: // 此处未向c中放入数据,所以程序一直在等待
        fmt.Println(v)
    case <- time.After(3 * time.Second): // time.After 返回一个channel,case被执行,输出 Timeout After 3 second
        fmt.Println("Timeout After 3 second")
    }

    /*
    // 程序在3s后的运行结果:
    Timeout After 3 second
     */

}
深入理解Go语言Channel:并发通信的核心机制 Channel是Go语言并发编程中的核心通信机制,基于CSP(Communicating Sequential Processes)理论实现。作为一种类型安全的管道,它通过发送和接收操作实现Goroutine间的同步与数据传递。底层采用环形队列和锁机制保证线程安全,支持缓冲和非缓冲两种模式。在分布式系统和高并发场景中,Channel能有效解决资源共享问题,避免竞态条件。通过select语句可实现多路复用,配合扇入(Fan-in)、扇出(Fan-out)等模式构建高效并发架构。文章详细解析了Channel的内 阅读详情

相关推荐

星宸科技SSD20x/SSD21X应用层DEMO实战指南:从入门到深度定制

本文详细介绍了星宸科技SSD20x/SSD21X系列芯片的应用层DEMO开发实战,从基础环境搭建到深度定制开发。内容涵盖DEMO资源获取、核心功能实现、第三方库集成以及性能优化技巧,帮助开发者快速掌握智能显示和多媒体处理应用的开发方法。特别适合嵌入式开发者在智能家居、工业控制等场景中应用。

weixin_42524226的博客 270

Go语言基础结构 —— Channel 通道

在Go语言中,channel(通道)是一种用于在goroutine之间进行通信和同步的特殊数据结构。它可以看作是一条管道,可以在不同的goroutine之间传递数据。使用通道,你可以在goroutine之间发送和接收值。通道提供了一种安全、同步的方式来共享数据。它确保在发送操作完成之前,接收操作会一直等待,并且在接收操作完成之前,发送操作也会一直等待。这种同步机制可以有效地避免并发访问共享数据时出现的竞争条件和数据竞争。Golang并发的核心哲学是不要通过共享内存进行通信。

xjx的博客 2476

Visual MODFLOW Flex 6.1新手入门:从数据导入到3D建模的完整工作流解析

本文为Visual MODFLOW Flex 6.1新手提供了从零开始构建地下水数值模型的完整工作流指南。详细解析了从项目规划、数据准备与导入,到构建三维概念模型、生成并运行数值模型,直至结果可视化与校准的核心步骤。帮助水文地质与环工领域初学者快速掌握这一专业软件,高效完成从数据到三维建模的全过程。

a1b2c的博客 812

go语法 — channel的基础用法

Channel 是 go 语言内置的一个非常重要的特性,也是 go 并发编程的两大基石之一(另一个是 go ,也就是 goroutine ),Go内建channel实现了go协程之间数据的读写相关操作。在 go 语言中,channel 就是 goroutine 之间通过通信来共享内存的手段。 channel 也作为一个队列,会保证数据收发顺序总是按照先入先出的原则进行,可以看作 go 程序内部的一个 FIFO 队列,一些 goroutine 向其中生产数据,另外一些消费数据,同时也会保证同一时刻有且仅有一个

qq_38658567的博客 4125

新年美食鉴赏——基于注意力机制CBAM的美食101分类

新年美食鉴赏——基于注意力机制CBAM的美食101分类一、数据预处理1.数据集介绍2.读取标签3.统一命名4.整理图片路径5.划分训练集与验证集6.定义美食数据集二、注意力机制1.简介2.卷积块注意力网络——CBAM通道注意力模块CAM空间注意力模块SAM3.将通道、空间注意力模块合并三、基于卷积块注意力模块的ResNet-CBAM1.代码实现2.模型测试3.查看网络结构4.模型训练5.模型预测四、总结与升华个人简介 春节是一年当中最隆重、最盛大的节日,吃得最好的一餐可能就是年夜饭了,想想就很激动呢!随着新

zbp_12138的博客 1万+

基于Web的智能家居(源码+开题)

实践上,智能家居系统的普及和应用将极大改善人们的居住体验,提高生活质量,同时也有助于促进智能家居产业链的发展和完善,为社会经济发展注入新的活力。此外,本研究还将对基于Web的智能家居系统的用户体验进行深入研究,通过用户调研和数据分析,不断优化系统功能和界面设计,提升用户的使用体验和满意度。针对以上问题,本研究将综合运用物联网、云计算、大数据等新一代信息技术,通过系统架构设计、功能模块开发、安全性保障、用户体验优化等多个方面的研究工作,力求为基于Web的智能家居系统的研发和应用提供全面而有效的解决方案。

sryue06的博客 1904

Go 并发之channel(通道)

通道类型的值本身就是并发安全的,这也是 Go 语言自带的、唯一一个可以满足并发安全性的类型。它使用起来十分简单。在声明并初始化一个通道的时候,需要用到 Go 语言的内建函数make。就像用make初始化切片那样,传给这个函数的第一个参数是代表了通道的具体类型的类型字面量。在声明一个通道类型变量的时候,首先要确定该通道类型的元素类型,这决定了可以通过这个通道传递什么类型的数据。比如,类型字面量chan int,其中的chan是表示通道类型的关键字,而int则说明了该通道类型的元素类型。

天天新知 4766

ADRV9009的学习历程(一)

** AD9361的同胞ADRV9009 ** 终于记录一下了 距离第一次想写博客记录自己学习的点点滴滴,大概已经过去有一年有余。一直都是停留在心有余而力不足的境界。 基本每次做项目遇到问题都会来优快云寻求帮助,这里虽离庙堂较远,但高手云集。每每读来,思绪万千,次次膜拜。 趁这次又学习了一样新物件,就记录一下,望大神看见能赐教一下。 ADRV9009 最近有项目要用到ADRV9009这个芯片。就来学习一下。做过AD9361的都知道,ADI官方会给出这个芯片的参考手册、数据手册和驱动程序。当然我只是见过AD

weixin_42868560的博客 1万+

Go中的channel

channel (1)channel本身是一个队列,先进先出 (2)线程安全,不需要加锁 (3)本身是有类型的,string, int 等,如果要存多种类型,则定义成 interface类型 (4)channel是引用类型,必须make之后才能使用,一旦 make,它的容量就确定了,不会动态增加!!它和map,slice不一样 特点: (1)一旦初始化容量,就不会改变了。 (2)当写满时,不可以写,取空时,不可以取。 1、关于 channel的声明和使用的代码: package main import (

始梦的少年的博客 1万+

STLink驱动故障全解析:从卡顿到重装的终极修复指南

本文全面解析STLink驱动故障的常见现象及解决方案,包括驱动卸载、重装、Keil环境配置等关键步骤。针对设备管理器黄色感叹号、Keil识别异常等问题,提供从软件到硬件的终极修复指南,帮助开发者高效解决STLink连接问题。

sss66的博客 739

14、深入解析Sigma - Delta调制算法:从原理到应用

本博客深入解析了Sigma-Delta调制(SDM)算法的原理与应用,特别是在Super Audio CD(SA-CD)音频编码中的作用。文章探讨了传统SDM的局限性,并介绍了一系列改进型前瞻式SDM技术,如Trellis Sigma-Delta调制、高效网格Sigma-Delta调制和修剪树Sigma-Delta调制。这些技术通过降低计算复杂度和提升信号转换质量,在音频处理和无损数据压缩领域展现出巨大潜力。此外,博客还对不同SDM技术进行了性能比较,并分析了如何实现最大信噪比(SNR),为未来音频技术和数

tgb34567890的博客 425

Go语言channel

channel是引用类型,使用的时候必须通过make进行初始化,make的channel打印结果是地址

行走的皮卡丘 2890

手把手教你学 NPU 驱动开发--第一部分:基础准备 开发环境搭建:Linux 内核编译与 NPU 仿真平台配置

摘要:本文详细介绍了搭建Linux内核编译与NPU仿真平台的完整流程。主要内容包括:1) 准备ARM64交叉编译工具链;2) 编译支持KGDB调试的Linux内核;3) 构建最小根文件系统;4) 使用QEMU模拟自定义NPU设备;5) 配置KGDB调试环境。文章重点讲解了如何通过修改设备树添加虚拟NPU设备,并提供了开源NPUIP Gemmini作为实验对象。最后展示了简单的"Hello NPU"驱动验证方法,为后续NPU驱动开发奠定了基础。文中还附有常用命令速查表,方便开发者快速搭建调

MHD0815的博客 384

《Concurrency in Go》阅读笔记 -- 第三章:Go语言并发组件

《Concurrency in Go》 本章节从goroutine入手,讲解go语言的各种并发原语。在讲解完goroutine之后,对于传统的内存同步访问的并发原语:sync包中的Mutex,RWMutex,Cond,Once,WaitGroup,Pool等进行了分析。在此之后着重讲了go语言的另一大特色:channel。在最后,讲解了如何结合channel的语法:select语句。 插一...

Shiina_Orez的博客 1222

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Go并发concurrency

并发concurrency  很多人都是冲着Go大肆宣扬的高并发而忍不住跃跃欲试,但其实从源码的解析来看,goroutine只是官方实现的超级“线程池”而已。不过话说回来,每个实例4-5KB的栈内存占用和由于实现机制而大幅减少的创建和销毁开销,是制造Go号称高并发的根本原因。另外,goroutine的简单易用,也在语言层面上给予了开发者巨大的便利。 并发不是并行: 并发主要由切换时间片来...

冷月醉雪的博客 637

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

GO语言学习笔记(十二、并发concurrency)

并发concurrency 很多人都冲着Go大肆宣扬的高并发而忍不住跃跃欲试,但其实从源码的解析来看,goroutine只是由官方实现的超级“线程池”而已。 不过话说回来,每个实例4-5KB的栈内存占用和由于实现机制而大幅减少的创建和销毁开销,是制造Go号称的高并发的根本原因。 另外goroutine的简单易用,也在语言层面上给予了开发者巨大的便利。 并发不是并行:Concurrency ...

分享AI、区块链技术及应用,未来已来 321

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

[11][go] go concurrency

绝妙的 CHANNEL channel1 channel提供了一种强大的、在不使用锁或临界区的情况下,从某个 goroutine 向其他 goroutine 发送数据流的方法。 发送者才能关闭channel 一个已经被关闭的 channel 永远都不会阻塞 已经被关闭的 channel。当一个 channel 一旦被关闭,就不能再向这个 channel 发送数据,不过你仍然可以尝试从 cha...

WhateverYoung的专栏 339

Go Concurrency

go 并发的一些概念

学习笔记 283

解锁 Go 并发超能力:Tutorials 用 5 个实战示例讲透 Goroutine、Channel 与 Select

🚀 Tutorials 是 Jake Wright YouTube 编程教程的配套源码仓库,其中 `go/02-go-concurrency` 目录用 **5 个由浅入深的 Go 并发实战示例**,帮你快速掌握 Goroutine、Channel 与 Select 三大核心机制。无需大段理论,跟着 5 个小文件就能建立完整的 Go 并发编程心智模型,是初学者难得的轻量级 Go 并发教程。 ##

gitblog_00531的博客 437

探索Go并发编程之美:Go-Concurrency项目解析

探索Go并发编程之美:Go-Concurrency项目解析 在这个高效率和多核处理器的时代,利用并发处理任务已成为软件开发的重要策略。Go语言以其独特的并发模型——goroutines和channels,为开发者提供了高效且易于管理并发的强大工具。而今天我们要介绍的项目,,是一个专门针对Go语言并发编程的学习资源库,旨在帮助开发者更好地理解和运用Go的并发特性。 项目简介 Go-Concurren...

gitblog_00038的博客 401

深度阅读之《Concurrency in Go》

很多朋友可能都知道,年前我从滴滴跑路,来了字节跳动,并且很快就写了篇传播甚广的弱智找 bug 文章:《“���”引发的线上事故》,之后就再也没动静了……当然最主要的原因还是这边工作的节奏...

梦醒人间 417

Golang基础_11-并发concurrency

目录 注意事项 计策一:给channel加buffer 计策二:引入sync包 select 例子:用goroutine实现发送接收数条消息 注意事项 goroutine只是官方的超级线程池 高并发性:占用内存小,创建销毁...

161

在go中监听多个channel

我们可以使用select关键字来同时监听多个goroutine。你可以看到select语句和switch语句在设计上非常相似。两者都定义了不同的情况,并在满足某种情况时运行相应的代码。另外,我们可以看到select语句是阻塞的。也就是说,它将等待,直到其中一个 case 被满足。我们为这个循环迭代了两次,因为只有两个goroutine需要监听。更确切地说,每个goroutine都是一个fire-and-forget goroutine,意味着它们在返回之前只向一个通道发送一次。

viviiuw的博客 40

golang select 选择器的两种方案

golang select 选择器 常规模式:

噬日者 1万+

Go并发编程实战第2版示例项目:深入理解Goroutine与Channel的10个实战技巧

Go并发编程是现代软件开发中的核心技能,而《Go并发编程实战第2版》的示例项目正是学习这一技术的绝佳资源。这个开源项目通过丰富的代码示例,系统性地展示了Go语言中goroutine和channel的10个关键实战技巧,帮助开发者从基础到高级全面掌握并发编程的精髓。 ## 📚 项目概览与快速入门 《Go并发编程实战第2版》示例项目是一个精心设计的Go语言学习资源,涵盖了从基础goroutine

gitblog_00345的博客 1287

Go lang concurrency: select with channels

package main import "fmt" func fibonacci(c, quit chan int) { x, y := 0, 1 for { select { case c <- x: x, y = y, x+y case <-quit: fmt.Println("

陌上 2260
上一篇: Go语言学习笔记 --- reflection
下一篇: Fix babel-preset-react-native TransformError
Wang's Blog
博客等级 码龄12年 7438粉丝 2422原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值