go语言中管道 channel的使用

创建 channel

声明的管道后,需要使用make函数初始化之后才能使用。

创建 channel 的格式如下

go 复制代码
ch := make(chan元素类型, 容量)  

管道有发送(send)、接收(receive) 和关闭(close)三种操作。

发送和接收都使用<-符号。

管道是先进先出的。

管道是一个引用数据类型。

现在我们先使用以下语句定义一个管道:

管道的使用

go 复制代码
package main
import (
	"fmt"
)
func main() {
	// 创建一个管道,管道的元素类型是int,容量是3
	ch := make(chan int, 3)
	// 向管道中发送数据,分别发送的数据是1,2,3
	ch <- 1
	ch <- 20
	ch <- 30
	a := <-ch
	fmt.Println(a)
	b := <-ch
	fmt.Println(b)
	c := <-ch
	fmt.Println(c)
}

输出的值是: 1 20 30 说明:管道是先进先出的

管道的容量,长度,引用地址

go 复制代码
package main
import (
	"fmt"
)
func main() {
	// 创建一个管道,管道的元素类型是int,容量是3
	ch := make(chan int, 3)
	// 向管道中发送数据,分别发送的数据是1,2,3
	ch <- 1
	ch <- 20
	fmt.Printf("管道的引用地址是:%v\n", ch)
	fmt.Printf("管道的长度是:%d\n", len(ch))
	fmt.Printf("管道的容量是:%d\n", cap(ch))
}

管道的引用地址是:0x1f9b2ce28000

管道的长度是:2

管道的容量是:3

管道的长度是当前管道中添加的数据的数量。

管道的容量是你设置的管道容量。 管道的引用地址是管道的内存地址。

证明管道是引用数据类型

go 复制代码
package main

import (
	"fmt"
)

func main() {
	// 创建一个管道,管道的元素类型是int,容量是3
	ch := make(chan int, 3)
	ch <- 1
	ch <- 20
	ch1 := ch
	// 向管道ch1中发送数据,如果ch管道也发生改变了,说明管道是引用数据类型
	ch1 <- 30
	fmt.Printf("ch1管道的长度是:%d\n", len(ch1))
	fmt.Printf("ch管道的长度是:%d\n", len(ch))

}

现在ch管道的长度是:3, ch1管道的长度也是:3

所以,可以证明管道是引用数据类型。

管道的阻塞(发送值)

go 复制代码
package main
import (
	"fmt"
)
func main() {
	// 创建一个管道,管道的元素类型是int,容量是2
	ch := make(chan int, 2)
	ch <- 1
	ch <- 20
	// 这个时候由于管道的容量是2,所以不能发送第三个数据,会造成管道的阻塞
	ch <- 30
	fmt.Printf("ch管道的长度是:%d\n", len(ch))
}

fatal error: all goroutines are asleep - deadlock!

管道的阻塞(获取值)

go 复制代码
package main

import (
	"fmt"
)

func main() {
	// 创建一个管道,管道的元素类型是int,容量是2
	ch := make(chan int, 2)
	ch <- 1
	ch <- 20
	value1 := <-ch
	value2 := <-ch
	// 这个时候由于管道的容量是2,所以不能获取第三个数据,会造成管道的阻塞
	value3 := <-ch
	fmt.Printf("value1是:%d\n", value1)
	fmt.Printf("value2是:%d\n", value2)
	fmt.Printf("value3是:%d\n", value3)
}

fatal error: all goroutines are asleep - deadlock!

for range遍历管道

使用for range遍历管道。

如果没有关闭管道,就使用 for range 遍历管道,

就会报个错误fatal error: all goroutines are asleep-deadlock!

通过for range来遍历管道数据管道,没有key,只有value。

关闭管道, for range才能遍历管道中的数据。

go 复制代码
package main
import (
	"fmt"
)
func main() {
	var ch1 = make(chan int, 4)
	ch1 <- 1
	ch1 <- 20
	ch1 <- 30
	ch1 <- 40
	// 关闭管道,才能遍历管道中的数据。否则会报错的
	close(ch1)
	// 循环遍历管道,注意:管道没有key,只有value
	for value := range ch1 {
		fmt.Printf("value是:%d\n", value)
	}
}

value是:1

value是:20

value是:30

value是:40

for循环取管道中的数据,可以不关闭管道

go 复制代码
package main
import (
	"fmt"
)
func main() {
	var ch1 = make(chan int, 4)
	ch1 <- 1
	ch1 <- 20
	ch1 <- 30
	ch1 <- 40
	// for循环取管道中的数据,可以不关闭管道
	for i := 0; i < 4; i++ {
		fmt.Printf("管道中数据是:%d\n", <-ch1)
	}
}

管道中数据是:1 管道中数据是:20 管道中数据是:30 管道中数据是:40

定义两个方法,一个方法给管道里面写数据,一个给管道里面读取数据。要求同步进行。

go 复制代码
package main

import (
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup

func writeData(ch chan int) {
	// 往管道里面写数据,写10个数据
	for i := 0; i < 10; i++ {
		ch <- i * 100
		fmt.Printf("写入管道中的数据是:%d,成功的\n", i*100)
		time.Sleep(time.Millisecond * 50)
	}
	// 关闭管道
	close(ch)
	wg.Done()
}
func readData(ch chan int) {
	for v := range ch {
		fmt.Printf("读取管道中的数据是:%d,成功的\n", v)
		time.Sleep(time.Millisecond * 50)
	}
	wg.Done()
}
func main() {
	var ch1 = make(chan int, 10)
	wg.Add(1)
	go writeData(ch1)
	wg.Add(1)
	go readData(ch1)
	wg.Wait()
	fmt.Println("程序结束")
}

读取管道中的数据是:0,成功的

写入管道中的数据是:0,成功的

... 读取管道中的数据是:900,成功的

程序结束

为啥先显示的是读取呢? 因为:打印的时候也会耗费一定的时候,

它可以先读取到,说明是先有数据的哈,才能够读取到数据。

所以是没有问题的。

如果写入非常的慢,读取非常的快,上面的程序会有什么问题?

回答:没有问题的。我们可以看下

当读取的速度比写入的速度快,读取的时候,就会进行等待哈。

go 复制代码
package main

import (
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup

func writeData(ch chan int) {
	// 往管道里面写数据,写10个数据
	for i := 0; i < 10; i++ {
		ch <- i * 100
		fmt.Printf("写入管道中的数据是:%d,成功的\n", i*100)
		// 写入数据的时候,等待100毫秒
		time.Sleep(time.Millisecond * 100)
	}
	// 关闭管道
	close(ch)
	wg.Done()
}
func readData(ch chan int) {
	for v := range ch {
		fmt.Printf("读取管道中的数据是:%d,成功的\n", v)
		// 读取数据的时候,等待50毫秒,现在读取的速度快,写入的速度慢
		time.Sleep(time.Millisecond * 50)
	}
	wg.Done()
}
func main() {
	var ch1 = make(chan int, 10)
	wg.Add(1)
	go writeData(ch1)
	wg.Add(1)
	go readData(ch1)
	wg.Wait()
	fmt.Println("程序结束")
}
相关推荐
Harvil_1 小时前
模型点三道菜,回喂被拒 400
后端
松就是我902981 小时前
如何设计多Agent
后端
海宇数科1 小时前
Go数据工程:利用海宇婚恋风险报告优化实名制严肃婚恋交友合规体验
人工智能·go
高频因子挖掘机1 小时前
盘中筛选要同时看价格和盘口?按需分层比“一次全拿”更好维护
后端·github·api
asong1 小时前
从写代码到部署上线,Cloudflare 给 AI 配了一把新钥匙
前端·javascript·后端
RobinDevNotes2 小时前
PagedAttention原理,KV缓存也能分页
人工智能·后端
卷无止境2 小时前
当AI写代码遇见Rust为什么会卡壳
后端·python
Percy_kk2 小时前
Django的CRUD映射
后端·django
用户EasyAdminBlazor2 小时前
Blazor Server 性能优化实战:Circuit、数据库、Redis 与并发
后端