Channel 使用事项和注意细节

(1)只读只写管道 

如果有个管道只允许其读,那么就声明为读。那么只写,声明为只写。

package main

import "fmt"

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

	//声明为只写管道
	var chan1 chan<- int
	chan1 = make(chan int, 3)
	fmt.Println(chan1)

	//声明为只读
	var chan2 <-chan int
	chan2 = make(chan int, 3)
	fmt.Println(chan2)
}

这里的管道类型仍然是chan int,只读只写并不代表是类型,而是代表一种属性。

package main

import "fmt"

func main() {
	var ch chan int
	var exitChan chan struct{}

	ch = make(chan int, 10)
	exitChan = make(chan struct{}, 2)

	go sender(ch, exitChan)
	go receive(ch, exitChan)

	fmt.Println(<-exitChan)
	fmt.Println(<-exitChan)
}

func sender(ch chan<- int, exitChan chan struct{}) {
	for i := 0; i < 10; i++ {
		ch <- i
	}
	close(ch)

	exitChan <- struct{}{}
}

func receive(ch <-chan int, exitChan chan struct{}) {
	for {
		if v, ok := <-ch; ok {
			fmt.Println(v)
		} else {
			break
		}
	}
	exitChan <- struct{}{}
}

这里主要将双向的管道ch = make(chan int, 10)传给了ch chan<- int这个形参之后,相当于将只读的操作不让你使用,但是类型依然是chan int。

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

要从管道当中读取数据需要close这个管道,不去close这个管道去遍历的时候,管道会阻塞,如果阻塞在main协程里面会发生死锁。

ch1 := make(chan int, 4)

for v := range ch1 {
   fmt.Println(v)
}

这里不去使用close去关闭管道

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
        C:/Users/W10/GolandProjects/goroutine/阻塞.go:18 +0x139

有些时候不好确定管道是否可以关闭,关闭的时机也不确定。

package main

import (
	"fmt"
	"time"
)

func main() {
	//使用select可以解决从管道当中读取数据阻塞的问题

	//1.定义一个管道 10个数据int
	initChan := make(chan int, 10)
	for i := 0; i < 5; i++ {
		initChan <- i
	}
	//2.定义一个管道 5个数据string
	stringChan := make(chan string, 5)
	for i := 0; i < 5; i++ {
		stringChan <- "hello" + fmt.Sprintf("%d", i)
	}

	//传统的方法在遍历管道的时候如果不关闭会阻塞导致死锁
	//在实际开发过程中,可能无法确定什么时候关闭该管道
	//可以使用select的方式可以解决

	for {
		select {
		//如果管道一直没有关闭,也不会一直阻塞在这里导致死锁
		// 它会自动的到下一个case匹配,不依赖关闭
		case v := <-initChan:
			time.Sleep(time.Second * 1)
			fmt.Printf("从intchan读取了数据%d\n", v)
		case v := <-stringChan:
			time.Sleep(time.Second * 1)
			fmt.Printf("从stringChan读取了数据%s\n", v)
		default:
			time.Sleep(time.Second * 1)
			fmt.Println("都取不到了,不玩了,这里可以加入业务逻辑")
		}
	}
}


从intchan读取了数据0
从intchan读取了数据1
从intchan读取了数据2
从intchan读取了数据3
从intchan读取了数据4
从stringChan读取了数据hello0
从stringChan读取了数据hello1
从stringChan读取了数据hello2
从stringChan读取了数据hello3
从stringChan读取了数据hello4
都取不到了,不玩了,这里可以加入业务逻辑
都取不到了,不玩了,这里可以加入业务逻辑
都取不到了,不玩了,这里可以加入业务逻辑
都取不到了,不玩了,这里可以加入业务逻辑

如果是在函数里面,直接使用return,那么就直接退出协程的函数。
 

(3)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 * 1)
		fmt.Println("func sayHello")
	}
}

// Test1  函数
func Test1() {
	//定义一个map
	var myMap map[int]string
	//这种写法是错误的,因为map是需要先make一下的
	myMap[0] = "golang" //这块协程报错导致整个程序崩溃
}

func main() {
	go sayHello()
	go Test1()
	time.Sleep(time.Second * 5)
	fmt.Println("func main")
}

panic: assignment to entry in nil map                            
                                                                 
goroutine 7 [running]:                                           
main.Test1()                                                     
        C:/Users/W10/GolandProjects/goroutine/recover.go:21 +0x25
created by main.main                                             
        C:/Users/W10/GolandProjects/goroutine/recover.go:26 +0x31

进程 已完成,退出代码为 2

显然影响了主线程的执行。这里可以使用错误处理机制recover+defer来解决。有些时候,一个协程崩溃了,我们并不希望它影响到其他的协程和主线程的执行。

修改过后的代码:

package main

import (
	"fmt"
	"time"
)

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

// Test1  函数
func Test1() {
	//这里可以使用错误处理机制defer + recover
	defer func() {
		//捕获test抛出的panic
		if err := recover(); err != nil {
			fmt.Println("Test1()发生异常", err)
		}
	}()
	//定义一个map
	var myMap map[int]string
	//这种写法是错误的,因为map是需要先make一下的
	myMap[0] = "golang" //这块协程报错导致整个程序崩溃
}

func main() {
	go sayHello()
	go Test1()
	time.Sleep(time.Second * 5)
	fmt.Println("func main")
}


Test1()发生异常 assignment to entry in nil map
func sayHello
func sayHello
func sayHello
func sayHello
func main
  • 17
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值