go语言中的只读/只写管道,多路复用select,捕获协程异常

创建一个只写管道

只写管道只能往管道里面写数据,不能从管道里面读取数据,

通过 chan<- 来创建一个只写管道

复制代码
package main
func main() {
	// 创建一个 int 类型的管道,只能写入数据
	chWrite := make(chan<- int, 1000)
	// 往管道里面写数据
	chWrite <- 100
}

创建一个只读管道

只读管道只能从管道里面读取数据,不能往管道里面写数据,

通过 <-chan 来创建一个只读管道

复制代码
package main

import "fmt"

func main() {
	// 创建一个只读的管道
	chRead := make(<-chan int, 1000)
	fmt.Println(chRead)
}

区分只读管道和只写管道

<- 在前面的是,只读管道; 如:make(<-chan int, 1000)

<- 在后面的是,只写管道; 如:make(chan<- int, 1000)

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

复制代码
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("程序结束")
}

select 语句

select 的使用类似于switch语句,它有一系列case分支和一个默认的分支。

每个case会对应一个管道的通信(接收或发送)过程。

select会一直等待,直到某个case的通信操作完成时,

就会执行case分支对应的语句。具体格式如下

注意点:使用select来获取channel里面的数据的时候不需要关闭channel。

关闭了反而会出现问题的哈。

复制代码
package main

import (
	"fmt"
)

func main() {
	intChan := make(chan int, 10)
	for i := 0; i < 5; i++ {
		intChan <- i
	}
	stringChan := make(chan string, 10)
	for i := 0; i < 5; i++ {
		stringChan <- "hello world " + fmt.Sprintf("%d", i)
	}
	// // 使用select来获取channel里面的数据的时候不需要关闭channel
	for {
		select {
		// 从intChan读取数据,随机的执行一个case
		case i := <-intChan:
			fmt.Println(i)
		// 从stringChan读取数据,随机的执行一个case
		case s := <-stringChan:
			fmt.Println(s)
		default:
			fmt.Println("数据读取完成")
			return
		}
	}
}

输出的如下:

hello world 0

0

1

2

3

hello world 1

4

hello world 2

hello world 3

hello world 4

数据读取完成

一个协程报错,不影响另外的协程

复制代码
package main

import (
	"fmt"
	"time"
)

func SayHello() {
	for i := 0; i < 5; i++ {
		time.Sleep(time.Microsecond * 50)
		fmt.Println("hello world")
	}
}

// 这个代码有问题,会报错
func Test() {
	var myMap map[int]string
	myMap[0] = "hello world"
}
func main() {
	go SayHello()
	go Test()
	// 防止主程序提前退出
	time.Sleep(time.Second)
}

现在的现象是:一个协程报错了,另外一个协程不会正常执行。

我们要来解决这个问题