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)
		}
	}
}
相关推荐
im_AMBER16 分钟前
学习日志05 python
python·学习
-凌凌漆-36 分钟前
【Qt】QStringLiteral 介绍
开发语言·qt
程序员爱钓鱼36 分钟前
Go语言项目工程化 — 常见开发工具与 CI/CD 支持
开发语言·后端·golang·gin
军训猫猫头1 小时前
1.如何对多个控件进行高效的绑定 C#例子 WPF例子
开发语言·算法·c#·.net
真的想上岸啊1 小时前
学习C++、QT---18(C++ 记事本项目的stylesheet)
开发语言·c++·学习
明天好,会的2 小时前
跨平台ZeroMQ:在Rust中使用zmq库的完整指南
开发语言·后端·rust
丁劲犇2 小时前
用 Turbo Vision 2 为 Qt 6 控制台应用创建 TUI 字符 MainFrame
开发语言·c++·qt·tui·字符界面·curse
rui锐rui2 小时前
大数据学习2:HIve
大数据·hive·学习
旷世奇才李先生2 小时前
Next.js 安装使用教程
开发语言·javascript·ecmascript
凛铄linshuo3 小时前
爬虫简单实操2——以贴吧为例爬取“某吧”前10页的网页代码
爬虫·python·学习