GO语言-管道channel

本文详细介绍了Go语言中的通道Channel,包括其作用、创建与使用、读写操作、关闭通道、范围循环以及缓冲通道。通道是goroutine间通信的重要方式,通过channel可以实现同步,并避免临界资源问题。同时,文章还提到了定向通道的概念,用于限制通道的读写方向。
摘要由CSDN通过智能技术生成

概述

        通道channel可以被认为是goroutine通信的管道。关于前一篇go语言锁的博客中也提到,解决临界资源问题可以使用锁,但是更建议的是使用channel来实现goroutine之间的通信。

不要通过共享内存来通信,而应该通过通信来共享内存。

  • 需要注意是,channel本身就是同步的。意味着同一时间只有一条goroutine来操作channel。
  • 死锁——如果一个channel只有读数据,而没有写数据,则会产生死锁。反之亦然。如果写代码时不小心造成了死锁,go语言会抛出相关异常。

channel通道的创建和使用

channel声明

每个通道都有其相关的类型,该类型就是通道内允许传输的数据类型。

空的通道为nil,nil的通道没有任何用处。所以通道的声明是和map类似的。

//声明通道
var channel_name chan type
//初始化通道
channel_name = make(chan type)

简短声明

channel_name := make(chan type)

channel读写操作

data := <- channel_name	//将通道中的值赋值给变量
channel_name <- data	//将变量的值写入通道

value, ok := <- channel_name //从一个channel中读取值

channel的读写操作都是阻塞的

正常情况下,如果不使用sync.WaitGroup同步等待组、锁,主函数中的子goroutine很可能在还没有执行时,因为主函数的结束而一起结束了。

但是可以通过“channel的读写操作都是阻塞的这一特性”,子goroutine通过channel通道传递信息给主goroutine,主goroutine在读到数据前会一直阻塞。

func main() {
   channel1 := make(chan bool)
   go func(){
      for i:=0;i<10;i++{
         fmt.Println(i)
      }
      channel1 <- true
      fmt.Println("子goroutine结束...")
   }()

   data1 := <- channel1
   fmt.Println("channel传递的值:",data1)
   fmt.Println("主函数结束...")
}

关闭channel通道

发送者可以通过关闭channel通道,告知接收方不会有更多的数据被发送到channel上了。

close(channel_name)

接受者可以接受来自channel读到数据时,使用额外的变量来检查通道是否关闭。

ok为true时,表示从通道中读取了一个value;当ok是false是,意味着正从一个封闭的通道读取数据,读取到的value都将是零值。

value, ok := <- channel_name

var channel1 = make(chan int)

func main() {
   go fun1()
   for{
	  fmt.Println("从通道中读取值...")
      v,ok := <-channel1
      time.Sleep(1*time.Second)
      if !ok {
         fmt.Println("channel读取结束,通道关闭",v,ok)
         break
      }
      fmt.Println("通道中的值:",v,ok)
   }
}

func fun1() {
   for i:=0;i<10;i++{
	  time.Sleep(1*time.Second)
	   fmt.Println("通道中写入值:", i)
      channel1 <- i
   }
   close(channel1)
}

channel通道的范围循环

通过for_range增强循环,不需要自己判断通道是否关闭。range会判断通道是否关闭。

var channel1 = make(chan int)

func main() {
   go fun1()
   for v := range channel1 { // v <- channel1
      time.Sleep(1 * time.Second)
      fmt.Println(v)
   }
}

func fun1() {
   for i := 0; i < 10; i++ {
      channel1 <- i
   }
   close(channel1)
}

缓冲通道

带缓冲区的通道,会把数据先写入缓冲区中。发送数据时,只有当缓冲区中满了才会被阻塞;接收数据时,只有缓冲区为空的时候才会被阻塞。

ch := make(chan type, Size)

Size需要大于0,使通道具有缓冲区。默认情况无缓冲区通道的容量为0,所以省略了该参数。

var channel1 = make(chan int,5)    //创建一个容量5的缓冲通道

func main() {
   go fun1()
   for v := range channel1 {
      time.Sleep(1 * time.Second)
      fmt.Println("\t读出数据",v)
   }
}

func fun1() {
   for i := 0; i < 10; i++ {
      channel1 <- i
      fmt.Println("写入数据:",i)
   }
   close(channel1)
}

定向通道

双向通道

前面我们创建的通道,都是双向通道。双向通道是可以,一个goroutine发送数据,一个goroutine接收数据的。

定向通道(单向通道)

单向通道也就是定向通道。单向通道只能发送或者接收数据。

定向通道创建语法

channel1 := make(chan <-int)	//只能写入数据,不能读数据
channel2 := make(<- chan int)	//只能读数据,不能写入数据

定向通道的用法

往往创建和使用通道的时候通常还是创建双向通道。单向通道只用来作为函数参数类型

只是在将通道作为参数传递到函数时,函数中通道的类型使用单向通道。限制通道在函数内部使用时是只能读不能写的;或者是只能写不能读的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值