
























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() }
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。