golang , chan学习

管道是阻塞的,管道的写法,管道内容读取,和写入

go 复制代码
package main

import "fmt"

func main() {
	// 管道声明 chan是关键字 int是类型
	// var chan1 chan int
	chan1 := make(chan int)
	fmt.Println(chan1)

	// 无缓冲通道,不会存储数据,会直接流入管道
	// 它的容量是 0,不能存储任何数据
	// 数据并不会在 channel 中做任何停留。这也意味着,无缓冲 channel 的发送和接收操作是同时进行的,它也可以称为同步 channel
	c := make(chan int)
	go func() {
		defer fmt.Println("goroutine over")
		fmt.Println("goroutine正在运行...")
		c <- 666 // 流入管道
	}()

	// 管道的读取是会被阻塞的
	// 接收:receive
	num, ok := <-c
	if ok {
		println("the chan is ok and num is ", num)
	}
	fmt.Println("main goroutine over")

}

带容量的chain,如果满了,那么发送的时候会阻塞,如果空了,那么接受的时候会阻塞

go 复制代码
package chandemo

import (
	"fmt"
	"time"
)

func bufferChannel() {
	// 带容量的chan
	c := make(chan int, 3)

	// len是实际的,cap是客观的容量
	fmt.Println("len(c) = , cap(c) = ", len(c), cap(c))

	go func() {
		defer fmt.Println("子go结束")

		// 轮询向管道发送数据
		// 如果队列已满,则阻塞等待,直到另一个 goroutine 执行,接收操作释放队列的空间
		for i := 0; i < 3; i++ {
			c <- i
			fmt.Println("子go程正在运行,发送的元素=i", i, " len(C) = ", len(c), "cap(c)", cap(c))
		}
	}()

	// 保证先发送完成
	time.Sleep(2 * time.Second)

	// 循环接收数据
	// 接收操作是从队列的头部获取元素并把它从队列中删除,如果队列为空,则阻塞等待,直到另一个 goroutine 执行,发送操作插入新的元素
	for i := 0; i < 3; i++ {
		num := <-c
		fmt.Println("num of i is ", num)
	}

	fmt.Println("main 结束")
}

单向管道,

  • 只能由发送方关闭管道:

    由发送方负责关闭管道,接收方不能关闭管道。

    如果接收方尝试关闭管道,会引发运行时错误。

  • 重复关闭会导致 panic:

    如果对同一个管道调用多次 close,会导致 panic。

    因此,关闭操作应确保在逻辑上只调用一次。

  • 关闭后的管道可以继续接收数据:

    但不能再向关闭的管道发送数据,否则会引发 panic。

go 复制代码
package chandemo

import "fmt"

// onlySend
func counter(out chan<- int) {
	for i := 0; i < 100; i++ {
		out <- i
	}
	// 发送完成后,关闭管道
	// 关闭管道可以向接收方表明,管道中不会再有新的数据。
	close(out)
}

// out是只能发送,in是只能接受
func squarer(out chan<- int, in <-chan int) {
	for i := range in {
		out <- i * i
	}
	close(out)
}

// 只能接收数据的管道
// in 为单向接收管道,用于从上游接收数据并打印
func printer(in <-chan int) {
	// 当管道关闭且数据读取完毕时,会自动退出循环
	for val := range in {
		fmt.Printf("收到数据: %d\n", val)
	}
}

func testSingleChan() {
	ch1 := make(chan int)
	ch2 := make(chan int)
	// ch1 -> ch2 -> printer
	// 启动counter,将数据发送给ch1
	go counter(ch1)
	// 启动squarer,将ch1的数据平方后发送给ch2
	go squarer(ch2, ch1)
	// 启动printer,从ch2接收数据并打印
	printer(ch2)
}

// 管道避免死锁
func main() {
    ch := make(chan int)

    go func() {
        ch <- 42 // 发送数据
        close(ch) // 关闭管道
    }()

    val, ok := <-ch
    fmt.Println(val, ok) // 42 true
    
	// 检查管道是否关闭
    val, ok = <-ch
    fmt.Println(val, ok) // 0 false (管道已关闭,没有数据)
}

select + chan实现多路复用

go 复制代码
func main() {

   //声明三个存放结果的channel
   firstCh := make(chan string)
   secondCh := make(chan string)
   threeCh := make(chan string)

   //同时开启3个goroutine下载
   go func() {
      firstCh <- downloadFile("firstCh")
   }()

   go func() {
      secondCh <- downloadFile("secondCh")
   }()

   go func() {
      threeCh <- downloadFile("threeCh")
   }()

   //开始select多路复用,哪个channel能获取到值,
   //就说明哪个最先下载好,就用哪个。
   select {
      case filePath := <-firstCh:
         fmt.Println(filePath)
      case filePath := <-secondCh:
         fmt.Println(filePath)
      case filePath := <-threeCh:
         fmt.Println(filePath)
   }
}

func downloadFile(chanName string) string {

   //模拟下载文件,可以自己随机time.Sleep点时间试试
   time.Sleep(time.Second)
   return chanName+":filePath"
}

// channel来退出
func testselectclose() {
	ch := make(chan int)

	go func() {
		for i := 1; i <= 3; i++ {
			ch <- i
		}
		close(ch)
		// ch <- 1// panic: send on closed channel
	}()

	for {
		select {
		case val, ok := <-ch:
			// 检测到false,因为close了,那么就会推出
			if !ok {
				fmt.Println("Channel closed, exiting.")
				return
			}
			fmt.Println("Received:", val)
		}
	}
}
相关推荐
骑着蜗牛撵大象3279 分钟前
大模型 Agent 的沙箱护栏:gVisor 隔离、出口白名单与审计闭环
java·开发语言·jvm·安全·agent沙箱·gvisor·microvm
Dxy12393102161 小时前
JS DES 加密详解
开发语言·javascript·arcgis
充值内卷1 小时前
我给桌面加了个鱼缸:C++ 写后端,WebView2 里养一座 Three.js 小岛
开发语言·javascript·c++
云和数据.ChenGuang1 小时前
深入解析 Python 的 dir():从用法到 CPython 源码实现
开发语言·人工智能·python·langchain
叼烟扛炮2 小时前
[Git 实战] 学习笔记:从生成 SSH 密钥到解决 TortoiseGit 推送报错全指南
linux·运维·笔记·git·学习
nice_lcj5202 小时前
深入理解 Java 反射:从 Class 对象到方法调用(含性能实测 + 迷你 IOC 实战 + 面试考点)
java·开发语言·后端·面试
蒸蒸yyyyzwd3 小时前
学习笔记 八股手撕
笔记·学习
樱花树下的数学喵4 小时前
手写简易 STL string|C++ 字符串底层实现
开发语言·c++
jimy14 小时前
C++ 报错的 qualifiers 的意思
开发语言·c++
留岁5 小时前
大模型推理部署 · 学习文档
学习