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

推荐订阅源

cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
WordPress大学
WordPress大学
宝玉的分享
宝玉的分享
人人都是产品经理
人人都是产品经理
博客园 - 聂微东
IT之家
IT之家
V
V2EX
Jina AI
Jina AI
V
Visual Studio Blog
有赞技术团队
有赞技术团队
博客园 - 司徒正美
博客园 - 叶小钗
The Cloudflare Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
小众软件
小众软件
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 三生石上(FineUI控件)
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Google DeepMind News
Google DeepMind News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
腾讯CDC
Google Online Security Blog
Google Online Security Blog
博客园 - 【当耐特】
Apple Machine Learning Research
Apple Machine Learning Research
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
N
News and Events Feed by Topic
N
News and Events Feed by Topic
The Last Watchdog
The Last Watchdog
W
WeLiveSecurity
月光博客
月光博客
Security Archives - TechRepublic
Security Archives - TechRepublic
Webroot Blog
Webroot Blog
SecWiki News
SecWiki News
博客园_首页
罗磊的独立博客
量子位
Latest news
Latest news
I
Intezer
V
Vulnerabilities – Threatpost
A
Arctic Wolf
Last Week in AI
Last Week in AI
Recent Commits to openclaw:main
Recent Commits to openclaw:main
S
SegmentFault 最新的问题
S
Security Affairs
阮一峰的网络日志
阮一峰的网络日志
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
酷 壳 – CoolShell
酷 壳 – CoolShell
P
Palo Alto Networks Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
N
News | PayPal Newsroom

博客园 - 悉野

ssh连结vmware中ubuntu的共享文件夹 spring加载冲突问题 spring一个错误修正 抓安卓日记到文件 注册表删除桌面顽固图标 github买的账号无法拉取代码的解决方法 apk常用命令 java服务器异常处理 ES6 学习难度分层 promise原理 材质, 纹理, shader之间的关系 TCP与UDP区别 javascript中=>与function的区别 javascript中函数解析过程 cocos使用fgui 几种服务注册与发现的区别 mybatis测试 向量点乘与叉乘 unity画布3种渲染模式 go学习笔记10(HTTP) go学习笔记9(TCP) go学习笔记8(反射) c++简单的线程池 operator new 是 C++ 动态内存分配的核心函数,负责分配原始内存,不调用构造函数 C++的定位放置new(Placement new) go学习笔记7(泛型,文件读写,测试) go学习笔记5(函数,结构体,自定义类型和类别名,接口) go学习笔记4(数组与切片,map,if,switch,for循环) go学习笔记3(变量定义,输入输出,基本数据类型) go学习笔记2 laya打包的apk版本更新脚本 laya列表滚动问题 laya踩坑记 laya列表时计算具体体子项是列表中第几个 子弹射击 laya给自己画边框 查看laya已经加载的资源 laya spine事件
go学习笔记6(协程与channel,select使用,线程安全,异常处理)
悉野 · 2026-04-05 · via 博客园 - 悉野

12.协程与channel

只读通道(<-chan):多用于通知外部、传递结果
只写通道(chan<-):多用于让外部往里塞任务 / 事件,内部消费

单独chan为可读写

外部只允许往里发任务,不允许读,所以返回 只写通道。

 

// 内部启动一个协程消费任务,对外只暴露只写通道

func NewWorkerPool() chan<- Job {
    jobCh := make(chan Job, 10)

    go func() {
        for job := range jobCh {
            // 内部消费
            doJob(job)
        }
    }()

    // 对外:你只能发,不能读
    return jobCh
}
sendOnlyCh := NewWorkerPool()
sendOnlyCh <- job1  // OK
// <-sendOnlyCh     // 编译报错:不能从只写通道接收
func NewEventCollector() chan<- Event {
    events := make(chan Event, 100)
    go consumeEvents(events)
    return events
}
package main

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

var moneyChan = make(chan int)
var nameChan = make(chan string)

// wg使用指针传递, 不然会是值传递, 变成复制了
func shopping(name string, money int, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Println(name, "start shopping")
    nameChan <- name
    moneyChan <- money
    time.Sleep(1 * time.Second)
    fmt.Println(name, "end shopping")
}

func main() {
    start := time.Now()
    // 等待组
    var wg sync.WaitGroup
    // 添加3组
    wg.Add(3)
    go shopping("a chick", 4, &wg)
    go shopping("a dog", 5, &wg)
    go shopping("a cat", 2, &wg)

    go func() {
        // 等待
        wg.Wait()
        close(nameChan)
        //close(moneyChan)
    }()

    go func() {
        var sum int
        // 两种方法遍历chan range或下面的循环读取chan时, 一定要先关闭, 不然会一直读, 直接卡住
        for v := range moneyChan {
            sum += v
        }
        fmt.Println("sum的值为:", sum)
    }()

    for name := range nameChan {
        fmt.Println(name)
    }

    //for {
    //    data, ok := <-moneyChan
    //    if !ok {
    //        break
    //    }
    //    sum = sum + data
    //}
    //chan可以不关闭, 一个协程发几次信号 ch <- true, true信号放到ch里面, 这边收几个信号<-ch, 从chn里面读出信号, 不存放

    elapsed := time.Since(start)
    fmt.Printf("elapsed: %s\n", elapsed)
}


13.select使用

package main

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

// select 通道操作多路复用
var moneyChan1 = make(chan int)
var nameChan1 = make(chan string)
var doneChan = make(chan int)

func send(name string, money int, wait *sync.WaitGroup) {
    fmt.Printf("%s开始购物\n", name)
    time.Sleep(time.Duration(1) * time.Second)
    fmt.Printf("%s结束购物\n", name)
    moneyChan1 <- money
    nameChan1 <- name
    wait.Done()
}

func main() {
    fmt.Printf("%T\n", time.After(time.Duration(1)*time.Second)) //<-chan time.Time 只能接收时间的通道
    var wait sync.WaitGroup
    start := time.Now()
    wait.Add(2)
    go send("abc", 2, &wait)
    go send("efg", 3, &wait)

    go func() {
        defer close(moneyChan1)
        defer close(nameChan1)
        wait.Wait()
        //doneChan <- 1
        close(doneChan) //close时, 发了整数0
    }()

    var moneyList []int
    var nameList []string
    for {
        select {
        case money := <-moneyChan1:
            moneyList = append(moneyList, money)
        case name := <-nameChan1:
            nameList = append(nameList, name)
        case data := <-doneChan:
            fmt.Println("结束了, 使用时间", time.Since(start), data)
            return
        }
    }
}

var done = make(chan bool)

func see() {
    fmt.Println("see开始")
    time.Sleep(time.Duration(2) * time.Second)
    fmt.Println("see结束执行")
    done <- true
}

// 协和超时处理
func testOutTime() {
    go see()
    select {
    case <-done:
        fmt.Println("done")
    case <-time.After(time.Duration(1) * time.Second):
        fmt.Println("timeout")
    }
}

// 1. 阻塞等待(无default时)
func test1() {
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() { time.Sleep(1 * time.Second); ch1 <- "来自ch1" }()
    go func() { time.Sleep(2 * time.Second); ch2 <- "来自ch2" }()

    select {
    case msg1 := <-ch1:
        fmt.Println(msg1) // 1秒后执行这里
    case msg2 := <-ch2:
        fmt.Println(msg2)
    }
    // 阻塞直到某个case可以执行
}

// 2. 非阻塞操作(有default时)
func test2() {
    ch := make(chan int)

    select {
    case v := <-ch:
        fmt.Println("收到:", v)
    default:
        fmt.Println("通道为空,不阻塞") // 立即执行这里
    }
}

// 3.随机选择(多个通道同时就绪)当多个 case 同时满足时,随机选择一个执行:
func test3() {
    ch1 := make(chan int, 1)
    ch2 := make(chan int, 1)

    ch1 <- 1
    ch2 <- 2

    select {
    case <-ch1:
        fmt.Println("ch1被选中") // 50%概率
    case <-ch2:
        fmt.Println("ch2被选中") // 50%概率
    }
}

// 使用模式
// 模式1:超时控制
func testModel1() {
    ch := make(chan string)

    go func() {
        time.Sleep(2 * time.Second)
        ch <- "结果"
    }()

    select {
    case result := <-ch:
        fmt.Println("收到:", result)
    case <-time.After(1 * time.Second): // 超时定时器
        fmt.Println("超时!") // 1秒后执行
    }
}

// 模式2:永久阻塞
func testModel2() {
    // 防止main退出,等待信号
    select {}
    // 或者
    for {
        select {} // 永久阻塞,不耗CPU
    }
}

// 模式3:优雅退出(quit通道)
func testModel3Worker(data <-chan int, quit <-chan bool) {
    for {
        select {
        case num := <-data:
            fmt.Println("处理数据:", num)
        case <-quit:
            fmt.Println("收到退出信号")
            return // 退出goroutine
        }
    }
}

func testModel3() {
    data := make(chan int)
    quit := make(chan bool)

    go testModel3Worker(data, quit)

    for i := 0; i < 3; i++ {
        data <- i
    }
    quit <- true
    time.Sleep(100 * time.Millisecond)
}

// 模式4:非阻塞通道操作
func testModel4() {
    ch := make(chan int, 2)
    ch <- 1

    // 尝试发送(缓冲区满时不阻塞)
    select {
    case ch <- 2:
        fmt.Println("发送成功")
    default:
        fmt.Println("通道已满,跳过")
    }

    // 尝试接收(无数据时不阻塞)
    select {
    case v := <-ch:
        fmt.Println("收到:", v)
    default:
        fmt.Println("无数据可用")
    }
}

// 模式5:批量处理 + 超时刷新
type BatchProcessor struct {
    data   chan string
    flush  chan bool
    buffer []string
}

func (bp *BatchProcessor) Run() {
    ticker := time.NewTicker(5 * time.Second) // 定期刷新
    defer ticker.Stop()

    for {
        select {
        case d := <-bp.data:
            bp.buffer = append(bp.buffer, d)
            if len(bp.buffer) >= 10 { // 满10条立即刷新
                bp.doFlush()
            }
        case <-ticker.C: // 时间到了,不管满不满都刷新
            if len(bp.buffer) > 0 {
                bp.doFlush()
            }
        case <-bp.flush: // 手动触发刷新
            bp.doFlush()
        }
    }
}

func (bp *BatchProcessor) doFlush() {
    fmt.Printf("刷新 %d 条数据: %v\n", len(bp.buffer), bp.buffer)
    bp.buffer = bp.buffer[:0] // 清空
}


14.线程安全

package main

import (
    "fmt"
    "sync"
)

func myAdd(a, b int) int {
    return a + b
}
func mySub(a, b int) int {
    return a - b
}

var data int

func testMutex() {
    var wait sync.WaitGroup
    wait.Add(2)
    var mu = sync.Mutex{}
    go func() {
        mu.Lock()
        for i := 0; i < 10000; i++ {
            data = myAdd(data, 1)
        }
        mu.Unlock()
        wait.Done()
    }()
    go func() {
        mu.Lock()
        for i := 0; i < 10000; i++ {
            data = mySub(data, 1)
        }
        mu.Unlock()
        wait.Done()
    }()
    wait.Wait()
    fmt.Print("data的值, ", data)
}

func testSyncMap() {
    var mapData = sync.Map{}
    go func() {
        for i := 0; i < 3; i++ {
            mapData.Store("a", 1)
        }
    }()
    go func() {
        for {
            var data, ok = mapData.Load("a")
            if ok {
                fmt.Println("mapData[\"a\"]=", data)
            } else { //else要与if的闭括号在同一行
                fmt.Println("mapData[\"a\"]=nil")
            }
            var actual, loaded = mapData.LoadOrStore("b", 2)
            if loaded {
                fmt.Println("mapData[\"b\"]=", actual)
            } else {
                fmt.Println("mapData[\"b\"] store new data")
            }
        }

    }()
}

func main() {
    testSyncMap()
    <-make(chan struct{}) // 是从新创建的 channel 中读取(接收)数据 “永久阻塞”, 类似select{}, 但必须有个goroutine存在
}


15.异常处理

package main

import (
    "errors"
    "fmt"
    "os"
    "runtime/debug"
)

// 错误处理方法, 1.抛出, 2.log.fatal, panic, 3.recover
func divide(a, b int) (int, error) {
    if b == 0 {
        //error.Error是内部接口类型, 可以获取error的原因
        return 0, errors.New("除数不能为0")
    }
    return a / b, nil
}

func testDivide(a, b int) (res int, err error) {
    res, err = divide(a, b)
    if err != nil {
        // 向上抛
        return
    }
    return
}

func test15() {
    data, err := os.ReadFile("./15.txt")
    if err != nil {
        // 初始化时, 异常panic可以输出堆栈
        panic(err)
    }
    fmt.Println(string(data))
}
func testRecover() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println(err)
            debug.PrintStack()
            //fmt.Println(string(debug.Stack()))
        }
    }()
    var data = []int{1, 25}
    fmt.Println(data[2])
}
func main() {
    testRecover()
    res, err := testDivide(2, 0)
    if err != nil {
        fmt.Println(err.Error())
    } else {
        fmt.Println(res)
    }
    // log.Fatal("异常了, 退出") //引函数会调用os.Exit
    //test15()
}