1)channel可以声明为只读,或只写性质

案例如下:

package main
import (
	_"fmt"
)

func main() {
	// 管道可以声明为只读或者只写
	// 1. 在默认情况下,管道是双向的
	// var chan1 chan int //  可读可写

	// 2. 声明为只写
	var chan2 chan<- int
	chan2 = make(chan int,3)
	chan2<- 20
	// num := <- chan2
	// fmt.Println(num)

	// 3. 声明为只读
	var chan3 <-chan int
	nm2 := <-chan3
}

2)channel 只读和只写的最佳实践案例

package main
import (
	"fmt"
)

// 只写操作
func send(ch chan<- int, exitChan chan struct{}) {
	for i := 0; i < 100; i++ {
		ch<- i
	}
	close(ch)
	var a struct{}
	exitChan <- a
}
// 只读操作
func recv(ch <-chan int,exitChan chan struct{}) {
	for {
		v, ok := <- ch
		if !ok {
			break
		}
		fmt.Println(v)
	}
	var a struct {}
	exitChan<- a
}
func main() {
	var ch chan int
	ch = make(chan int, 10)
	exitChan := make(chan struct{}, 2)
	go send(ch, exitChan)
	go recv(ch, exitChan)
	var total = 0
	for _ = range exitChan {
		total++
		if total == 2 {
			break
		}
	}
	fmt.Println("结束")
}

3)使用select 解决从管道取数据的阻塞问题

package main
import (
	"fmt"
)

func main() {
	// 使用 select解决从管道取数据的阻塞问题
	// 1. 定义一个管道 10个数据int
	intChan := make(chan int,10)
	for i := 0; i < 10; i++ {
		intChan<- i
	}
	// 2. 定义一个管道 5个数据string
	stringChan := make(chan string, 5)
	for i := 0; i < 5; i++ {
		stringChan<- "hello" + fmt.Sprintf("%d",i)
	}
	// 传统的方法在遍历管道时,如果不关闭会阻塞而导致deadlock
	// 问题:在实际开发中,可能不好确定什么时候关闭管道
	// 可以使用select 方式解决
	label:
	for {
		select {
			case v := <-intChan : // 注意:这里,如果intChan一致没有关闭,不会一直阻塞而deadlock
			// 会自动地到下一个case匹配
				fmt.Printf("从intChan读取的数据%d\n",v)
			case v := <- stringChan:
				fmt.Printf("从stringChan读取的数据%v\n",v)
			default:
				fmt.Printf("都取不到了,退出...")
				break label
		}
	}
}

4)goroutine中使用recover,解决协程中出现panic,导致程序崩溃的问题

说明:如果我们起了一个协程,但是这个协程出现了panic,如果我们没有捕获这个panic,就会造成整个程序的崩溃,这时我们可以在goroutine中使用recover来捕获panic进行处理,这样即使这个协程发生问题,但是主线程仍然不受影响,可以继续执行

代码实现:

package main
import (
	"fmt"
	"time"
)

// 函数
func sayHello() {
	for i := 0; i < 10; i++ {
		time.Sleep(time.Second)
		fmt.Println("hello, world")
	}
}

// 函数
func test() {
	// 这里我们可以使用 defer + recover
	defer func() {
		// 捕获test中抛出的panic
		if err := recover();err != nil {
			fmt.Println("test() 发生错误", err)
		}
	}()
	// 定义了一个map
	var myMap map[int]string
	myMap[0] = "golang" // error
}
func main() {
	go sayHello()
	go test()
	for i := 0; i < 10; i++ {
		fmt.Println("main() ok=",i)
		time.Sleep(time.Second)
	}
}