Go语言中的channel

CaiGuangyin · · 1036 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

一. channel

  • 线程通信在每个编程语言中都是重难点,在Golang中提供了语言级别的goroutine之间通信:channel
  • channel不同的翻译资料叫法不一样.常见的几种叫法
    • 管道
    • 信道
    • 通道
  • channel是进程内通信方式,每个channel只能传递一个类型的值.这个类型需要在声明channel时指定
  • channel在Golang中主要的两个作用
    • 同步
    • 通信
  • Go语言中channel的关键字是chan
  • 声明channel的语法
var 名称 chan 类型
var 名称 chan <- 类型 //只写
var 名称 <- chan 类型//只读
名称:=make(chan int) //无缓存channel
名称:=make(chan int,0)//无缓存channel
名称:=make(chan int,100)//有缓存channel
  • 操作channel的语法:(假设定义一个channel名称为ch)
ch <- 值 //向ch中添加一个值
<- ch //从ch中取出一个值
a:=<-ch //从ch中取出一个值并赋值给a
a,b:=<-ch//从ch中取出一个值赋值给a,如果ch已经关闭或ch中没有值,b为false

二. 代码示例

  • 简单无缓存通道代码示例
    • 此代码中如果没有从channel中取值c,d=<-ch语句,程序结束时go func并没有执行
    • 下面代码示例演示了同步操作,类似与WaitGroup功能,保证程序结束时goroutine已经执行完成
    • 向goroutine中添加内容的代码会阻塞goroutine执行,所以要把ch<-1放入到goroutine有效代码最后一行
    • 无论是向channel存数据还是取数据都会阻塞
    • close(channel)关闭channel,关闭后只读不可写
package main
 
import (
   "fmt"
)
 
func main() {
   ch := make(chan int)
   go func() {
      fmt.Println("进入goroutine")
      // 添加一个内容后控制台输出:1 true
      //ch<-1
 
      //关闭ch控制台输出:0 false
      close(ch)
   }()
   c, d := <-ch 
   fmt.Println(c, d)
   fmt.Println("程序执行结束")
}
  • 使用channel实现goroutine之间通信
    • channel其实就是消息通信机制实现方案,在Golang中没有使用共享内存完成线程通信,而是使用channel实现goroutine之间通信.
package main
 
import (
   "fmt"
)
 
func main() {
   //用于goroutine之间传递数据
   ch := make(chan string)
   //用于控制程序执行
   ch2 := make(chan string)
   go func() {
      fmt.Println("执行第一个goroutine,等待第二个goroutine传递数据")
      content := <-ch
      fmt.Println("接收到的数据为:", content)
      ch2 <- "第一个"
   }()
   go func() {
      fmt.Println("进入到第二个,开始传递数据")
      ch <- "内容随意"
      close(ch)
      fmt.Println("发送数据完成")
      ch2 <- "第二个"
   }()
   result1 := <-ch2
   fmt.Println(result1, "执行完成")
   result2 := <-ch2
   fmt.Println(result2, "执行完成")
   fmt.Println("程序执行结束")
}
  • 可以使用for range获取channel中内容
    • 不需要确定channel中数据个数
func main() {
   ch:=make(chan string)
   ch2:=make(chan int)
   go func() {
      for i:=97;i<97+26;i++{
         ch <- strconv.Itoa(i)
      }
      ch2<-1
   }()
 
   go func() {
      for c := range ch{
         fmt.Println("取出来的",c)
      }
   }()
   <-ch2
   fmt.Println("程序结束")
}
  • channel是安全的.多个goroutine同时操作时,同一时间只能有一个goroutine存取数据
package main
 
import (
   "time"
   "fmt"
)
 
func main() {
   ch := make(chan int)
 
   for i := 1; i < 5; i++ {
      go func(j int) {
         fmt.Println(j, "开始")
         ch <- j
         fmt.Println(j, "结束")
      }(i)
   }
 
   for j := 1; j < 5; j++ {
      time.Sleep(2 * time.Second)
      <-ch
   }
}

三. 死锁

  • 在主goroutine中向无缓存channel添加内容或在主goroutine中向channel添加内容且添加内容的个数已经大于channel缓存个数就会产生死锁
fatal error : all goroutines are asleep -deadlock!
  • 死锁:在程序中多个进程(Golang中goroutine)由于相互竞争资源而产生的阻塞(等待)状态,而这种状态一直保持下去,此时称这个线程是死锁状态
  • 在Golang中使用无缓存channel时一定要注意.以下是一个最简单的死锁程序
    • 主协程中有ch<-1,无缓存channel无论添加还是取出数据都会阻塞goroutine,当前程序无其他代码,主goroutine会一直被阻塞下去,此时主goroutine就是死锁状态
func main() {
   ch := make(chan int)
   ch <- 1
}
  • 而下面代码就不会产生死锁
    • 通过代码示例可以看出,在使用无缓存channel时,特别要注意的是在主协程中有操作channel代码
package main
 
import (
   "time"
   "fmt"
)
 
func main() {
   ch := make(chan int)
   go func() {
      ch <- 1
      fmt.Println("执行goroutine")
   }()
   time.Sleep(5e9)
   fmt.Println("程序执行结束")
}

四. 有缓存通道

  • 创建一个有缓存通道
func main() {
   ch := make(chan int, 3) //缓存大小3,里面消息个数小于等于3时都不会阻塞goroutine
   ch <- 1
   ch <- 2
   ch <- 3
   ch <- 4 //此行出现死锁,超过缓存大小数量
}
  • 在Golang中有缓存channel的缓存大小是不能改变的,但是只要不超过缓存数量大小,都不会出现阻塞状态
package main
 
import "fmt"
 
func main() {
   ch := make(chan int, 3) //缓存大小3,里面消息个数小于等于3时都不会阻塞goroutine
   ch <- 1
   fmt.Println(<-ch)
   ch <- 2
   fmt.Println(<-ch)
   ch <- 3
   ch <- 4
   fmt.Println(len(ch))//输出2,表示channel中有两个消息
   fmt.Println(cap(ch))//输出3,表示缓存大小总量为3
}

有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:CaiGuangyin

查看原文:Go语言中的channel

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1036 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传