您现在的位置是:首页 >其他 >Go-channel的妙用网站首页其他

Go-channel的妙用

技术鱼 2024-07-12 18:01:02
简介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 用于协程间通讯,必须存在读写双方,否则将造成死锁

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。