golang学习笔记--管道 channel

目录

【1】定义一个管道

【2】向管道写入数据

【3】 从管道读取数据

【4】使用空接口的管道

【5】关闭管道

【6】遍历管道

【7】协程和管道的应用例子 

【8】管道阻塞

【9】管道多路复用


【1】定义一个管道

package main

import "fmt"

func main() {
	//定义一个管道只能存入int类型的数据
	var intChan chan int
	//管道必须make才能使用
	intChan = make(chan int, 3)

	fmt.Printf("intChan 的值是%v,地址是%p", intChan, &intChan)
}

可以看出管道是引用类型,他的值是内存中真正的管道的地址

【2】向管道写入数据

管道的长度不能超过其容量否则会报以下错误

all goroutines are asleep - deadlock!

package main

import "fmt"

func main() {
	//定义一个管道只能存入int类型的数据
	var intChan chan int

	//管道必须make才能使用
	intChan = make(chan int, 3)

	//向管道写入数据
	intChan <- 10
	num := 5
	intChan <- num
	//看看管道的长度和cap(容量)
	fmt.Printf("intChan len = %v,cap = %v\n", len(intChan), cap(intChan))
}

【3】 从管道读取数据

 管道的数据是先进先出的,所以第一个放入的数据也会被第一个读取

注意:读取时如果数据已被取完还继续取时会报以下错误

all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:

package main

import "fmt"

func main() {
	//定义一个管道只能存入int类型的数据
	var intChan chan int

	//管道必须make才能使用
	intChan = make(chan int, 3)

	// fmt.Printf("intChan 的值是%v,地址是%p\n", intChan, &intChan)

	//向管道写入数据
	intChan <- 10
	num := 5
	intChan <- num
	intChan <- 66
	//看看管道的长度和cap(容量)
	fmt.Printf("intChan len = %v,cap = %v\n", len(intChan), cap(intChan))

	//从管道中读取数据
	var num2 int
	num2 = <-intChan
	num3 := <-intChan
	<-intChan//直接取出不接收
	fmt.Println("num2 = ", num2)
	fmt.Println("num3 = ", num3)
	fmt.Printf("intChan len = %v,cap = %v\n", len(intChan), cap(intChan))

}

golang学习笔记--管道 channel_第1张图片

【4】使用空接口的管道

使用空接口可以存入任何类型的数据,但是取出时必须使用类型断言

package main

import "fmt"

type Cat struct {
	Name string
	Age  int
}

func main() {
	allChan := make(chan interface{}, 10)
	cat1 := Cat{"tom", 10}
	cat2 := Cat{"jack", 18}
	allChan <- cat1
	allChan <- cat2
	allChan <- 30
	allChan <- "lucy"

	<-allChan
	catRes2 := (<-allChan).(Cat)
	fmt.Printf("catRes的类型是%T,值是%v\n", catRes2, catRes2)
	fmt.Println(catRes2.Name)

}

 

【5】关闭管道

使用内置函数 close() 可以关闭 channel, 当 channel关闭后,就不能再向 channel写数据了,但是仍然可以从该 channel读取数据

golang学习笔记--管道 channel_第2张图片

【6】遍历管道

channel支持for--range的方式进行遍历,请注意两个细节
1、在遍历时,如果channel没有关闭,则会出现deadlock的错误

2、在遍历时,如果channel已经关闭,则会正常遍历数据,遍历完后,就会退出遍历。

package main

import "fmt"

func main() {
	intChan := make(chan int, 200)
	for i := 0; i < 100; i++ {
		intChan <- i * 2
	}
	close(intChan)
	for v := range intChan {
		fmt.Println(v)
	}
}

 golang学习笔记--管道 channel_第3张图片

【7】协程和管道的应用例子 

示例1:

goroutine和channel协同工作的案例

1、 开启一个writeData协程,向管道intChan中写入200个整数

2、开启一个readData协程,从管道intChan中读取writeData写入的数据。

3、注意: writeData和readDate操作的是同一个管道

4、主线程需要等待writeData和readDate协程都完成工作才能退出

 golang学习笔记--管道 channel_第4张图片

package main

import "fmt"

func writeData(intChan chan int) {
	for i := 1; i <= 200; i++ {
		intChan <- i
		fmt.Println("writeData写入数据 = ", i)
	}
	close(intChan)
}

func readData(intChan chan int, exitChan chan bool) {
	for {
		v, ok := <-intChan
		if !ok {
			break
		}
		fmt.Println("readData读到数据 = ", v)
	}
	//读完数据后,任务完成,向exitChan中写入一个true
	exitChan <- true
	close(exitChan)
}

func main() {
	intChan := make(chan int, 200)
	exitChan := make(chan bool, 1)
	go writeData(intChan)
	go readData(intChan, exitChan)
	for {
		_, ok := <-exitChan
		if !ok {
			break
		}
	}
}

golang学习笔记--管道 channel_第5张图片

示例2: 通过阻塞完成协程的信息通信

package _case

import "fmt"

func Communication() {
	ch := make(chan int, 20)
	go communicationF1(ch)
	go communicationF2(ch)
}

// 接收一个只写通道
func communicationF1(ch chan<- int) {
	for i := 0; i < 100; i++ {
		ch <- i
	}
	close(ch)
}

// 接收一个只读通道
func communicationF2(ch <-chan int) {
	for v := range ch {
		fmt.Println(v)
	}
}
package main

import (
	"os"
	"os/signal"
	_case "project01/channel-select/case"
)

func main() {
	_case.Communication()

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt, os.Kill)
	<-ch
}

golang学习笔记--管道 channel_第6张图片

【8】管道阻塞

如果只向管道写入数据,而没有读取,就会出现阻塞而deadlock,原因是intChan容量是10,而代码writeData会写入 50个数据,因此会阻塞在 wiiteData的 ch<-i

1、如果,编译器(运行),发现一个管道只有写,而没有读,则该管道,会阻塞。
2、写管道和读管道的频率不一致,无所谓。

【9】管道多路复用

// 通知协程退出与多路复用
func NoticeAndMultiplexing() {
	ch := make(chan int, 1)
	strCh := make(chan string, 1)
	done := make(chan struct{}, 1)
	go NoticeAndMultiplexingF1(ch)
	go NoticeAndMultiplexingF2(strCh)
	go NoticeAndMultiplexingF3(ch, strCh, done)
	time.Sleep(5 * time.Second)
	close(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
			// default:
			// 	fmt.Println("不阻塞,进入死循环")
		}
		i++
		fmt.Println("累计执行次数:", i)
	}
}

调用: 

package main

import (
	"os"
	"os/signal"
	_case "project01/channel-select/case"
)

func main() {
	_case.NoticeAndMultiplexing()

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt, os.Kill)
	<-ch
}

执行结果:

golang学习笔记--管道 channel_第7张图片

你可能感兴趣的:(golang,#,学习笔记,golang,学习,笔记,开发语言,后端)