Go-channel的妙用

简介: Go-channel的妙用

系列文章目录


异常处理(defer recover panic)

Go-channel的妙用


前言


Go语言中,各个协程之间的通信,Go 语言协程之间通信的理念通过通信去共享内存。就是采用channel 技术实现。


一、channel 通过通讯共享内存


  1. channel的方向, 读、写、读写;
  2. channel 协程间通信信道;
  3. channel 阻塞协程;
  4. channel 并发场景下的同步机制;
  5. channel 通知协程退出;
  6. channel 的多路复用; 借助于select监听,channel阻塞在select ,


二、使用场景


  1. 协程间通信,即协程间数据传输;
  2. 并发场景下利用channel的阻塞机制,作为同步机制(类似队列);例如并发打印日志,可以把并发写日志请求写入channel,然后使用另个一协程在读取channel 中请求,写日志。
  3. 利用channel关闭时发送广播的特性,作为协程退出通知;channel 关闭的时候,会向所有监听它的协程发送一个零值。


三、例子


1.包

代码如下(示例):case/channel.go

package _case
import (
  "fmt"
  "time"
)
// 协程间通信
func Communication() {
  // 定义一个可读可写的通道
  ch := make(chan int, 0)
  go communicationF1(ch)
  go communicationF2(ch)
}
// F1接受一个只写通道
func communicationF1(ch chan<- int) {
  // 通过循环向通道写入0~99
  for i := 0; i < 99; i++ {
    ch <- i
  }
}
// F1接受一个只读通道
func communicationF2(ch <-chan int) {
  // 通过循环向通道写入0~99
  for i := range ch {
    fmt.Println(i)
  }
}
// 并发场景下的同步机制
func ConcurentSync() {
  //带缓冲的通道 chan 带10个缓存,可以并发写入10个,写满后阻塞,只有读出后才能狗写入
  ch := make(chan int, 10)
  // 向chan 写入数据
  go func() {
    for i := 0; i < 100; i++ {
      ch <- i
    }
  }()
  // 向chan 写入数据
  go func() {
    for i := 0; i < 100; i++ {
      ch <- i
    }
  }()
  // 从chan 中读取数据
  go func() {
    for i := range ch {
      fmt.Println(i)
    }
  }()
}
// 通知协程退出,多路复用
func NoticeAndMultiplexing() {
  ch := make(chan int, 0)
  strCh := make(chan string, 0)
  done := make(chan struct{}, 0)
  go noticeAndMultiplexingF1(ch)
  go noticeAndMultiplexingF2(strCh)
  go noticeAndMultiplexingF3(ch, strCh, done)
  time.Sleep(5 * time.Second)
  close(done) // 关闭done 时候会向所有监听它的协程发送一个零值。
}
func noticeAndMultiplexingF1(ch chan<- int) {
  for i := 0; i < 100; i++ {
    ch <- i
  }
}
func noticeAndMultiplexingF2(ch chan<- string) {
  for i := 0; i < 100; i++ {
    ch <- fmt.Sprintf("数字:%d", i)
  }
}
// select 子句作为一个整体阻塞,其中任意channel 准备就绪则继续执行
func noticeAndMultiplexingF3(ch <-chan int, strCh <-chan string, done <-chan struct{}) {
  i := 0
  for {
    select {
    case i := <-ch:
      fmt.Println(i)
    case str := <-strCh:
      fmt.Println(str)
    case <-done:
      fmt.Println("收到退出通知,退出当前协程")
      return
    }
    i++
    fmt.Println("累计执行次数: ", i)
  }
}
代码如下(示例):main.c
```c
package main
import (
  _case "channel-select/case"
  "os"
  "os/signal"
)
func main() {
  //_case.Communication()
  //_case.ConcurentSync()
  _case.NoticeAndMultiplexing()
  ch := make(chan os.Signal, 0)
  signal.Notify(ch, os.Interrupt, os.Kill) //ctr+c 或kill 时候往channel 中写入信号量
  <-ch                                     // 从 ch 中读取数据,数据内容不关心,只要有信号意味着系统退出了,没有值读出就是阻塞到这里
}

总结


注意: channel 用于协程间通讯,必须存在读写双方,否则将造成死锁

目录
相关文章
|
3月前
|
缓存 并行计算 Go
go 语言中 channel 的简单介绍
go 语言中 channel 的简单介绍
|
5月前
|
存储 Go
Go 语言当中 CHANNEL 缓冲
Go 语言当中 CHANNEL 缓冲
|
6月前
|
存储 缓存 安全
Go Channel详解
Go Channel详解
130 0
|
安全 Go
Go 语言学习之 goroutine 和 channel
Go 语言学习之 goroutine 和 channel
36 0
|
存储 缓存 Go
Go channel结构剖析《一》
Go channel结构剖析《一》
|
监控 Java Go
Go channel结构剖析《三》
Go channel结构剖析《三》
|
存储 Go 索引
Go channel结构剖析《二》
Go channel结构剖析《二》
|
Java Go
Go_Channel详解
Go_Channel详解
96 0
|
存储 缓存 Java
浅析Go中Channel的各路用法
浅析Go中Channel的各路用法
31567 0
浅析Go中Channel的各路用法
|
Go
【Golang】panic和recover底层逻辑实现|Go主题月
在每个goroutine也有一个指针指向_panic链表表头,然后每增加一个panic就会在链表头部加入一个_panic结构体。当所有的defer执行完后,_panic链表就会从尾部开始打印panic信息了,也就是说先发生的panic先打印信息。
236 0