Golang channe你知多少

haicoder_ibai · · 479 次点击 · 开始浏览    置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。
> 原文地址:**[嗨客网](https://haicoder.net/note/golang/golang-channel-usage.html)** # Golang channel使用 ## Golang channel使用教程 在 **[golang](https://haicoder.net/golang/golang-tutorial.html)** 中 **[channel](https://haicoder.net/golang/golang-channel.html)** 用于 **[goroutine](https://haicoder.net/golang/golang-goroutine.html)** 之间的通信,channel 是引用类型,需要使用 make 来创建 channel。在 Golang 中,用通信来共享内存,而不要用共享内存来通信。 ## channel创建详解 ### 语法 ```go ch := make(chan Type, [buffer]) ``` ### 参数 **ch**: 创建的 channel 类型的变量 **Type**: channel 的类型 **buffer**: channel 缓冲区大小 ### 说明 make 对于 channel 接收两个参数,第一个是通道的类型,第二个是个可选参数代表通道缓冲区的大小(省略代表无缓冲)。 ### 无缓冲channel ```go ch := make(chan int) ``` ### 带缓冲channel ```go ch := make(chan int, 2) ``` ## channel写入数据 ### 说明 我们可以使用 `<-` 符号指向 channel 来将元素放入 channel 中,注意向通道中传值必须要求该通道还有容量(缓冲),而且通道不能关闭。 对于无缓冲的或者缓冲已经满了的 channel 不可以轻易的传入值,必须要有 goroutine 同时在取元素才可以放入。 ### 示例 向一个有缓冲,非满的 channel 传值 ```go c := make(chan int, 1) // 定义一个带有一个缓冲的通道 c <- 1 // 向通道中传入一个 1,正常 ``` 向一个有缓冲,满的 channel 传值 ```go c := make(chan int, 1) // 定义一个带有一个缓冲的通道 c <- 1 // 向通道中传入一个值,这个值传入后填满了该通道 c <- 2 // 再向通道中传入一个值,报错 ``` 向一个无缓冲的 channel 传值 ```go c := make(chan int) // 定义一个无缓冲通道 c <- 1 // 向无缓冲通道传值,报错 ``` 其实上述两种错误均是由于通道满了而引起的(无缓冲的通道可以看成是缓冲为 0 的通道),解决方法很简单,只要保证有个 goroutine 同时在从该通道中取值即可。 ## channel读取数据 ### 说明 我们可以使用 `<-` 符号指向变量来将 channel 中的元素放变量中,此时可以接收两个值一个数值一个状态,如下代码: ```go v, ok := <-c // c 是通道,v 是取到的值,ok 是状态,正常时是 true,从关闭的空通道取值是 false ``` 对于无缓冲的或者缓冲已经空了的 channel 不可以轻易的取出值,必须要同时在放元素才可以取出。 ### 示例 ok 为 true 的例子 ```go c := make(chan int, 1) c <- 1 a, ok := <-c fmt.Println(a, ok) // 输出 1 true ``` ok 为 false 的例子 ```go c := make(chan int, 1) close(c) a, ok := <-c fmt.Println(a, ok) // 输出 0 false ``` ok 的应用--循环取值 ```go for { v, ok := <- c if !ok { break } fmt.Println(v) } ``` range 取值 ```go func main() { c := make(chan int, 10) for i:=0; i< 10; i++ { c <- i } close(c) for v := range c{ fmt.Println(v) } } ``` 向一个有缓冲,非空的 channel 取值 ```go c := make(chan int, 1) // 定义一个带有一个缓冲的通道 c <- 1 // 向通道中传入一个1,使通道非空 i := <-c // 从通道中取出一个值赋给变量i // 如果只是想取出值而不想对该值做任何其他操作,可以这么写 <-c 左边省略接收者 ``` 向一个有缓冲,空的 channel 取值 ```go c := make(chan int, 1) // 定义一个带有一个缓冲的通道 <-c // 向空通道中取出一个值,报错 ``` 向一个无缓冲的 channel 取值 ```go c := make(chan int) // 定义一个无缓冲通道 <-c // 向无缓冲通道取值,报错 ``` 其实上述两种错误均是由于通道空了而引起的(无缓冲的通道可以看成是缓冲为0的通道),解决方法很简单,只要保证同时在从该通道中存值即可。 ## 关闭channel详解 ### 说明 对于一个通道我们可以使用 close 内置函数来进行关闭,关闭后的通道具有以下特点: - 向一个已经关闭的通道发送值是不允许的,会报错 - 从一个已经关闭但是里面还有值的通道取值是允许的,可以正常获取到值 - 从一个已经关闭但是为空的通道取值是允许的,会获取通道类型元素的零值 - 不可以再次关闭一个已经关闭的通道,会报错 - 已经关闭的通道无法再次打开 ### 示例 向一个已经关闭的通道发送值 ```go c := make(chan int, 1) close(c) c <- 1 // 报错 ``` 从一个已经关闭但是里面还有值的通道取值 ```go c := make(chan int, 1) c <- 1 close(c) a := <-c fmt.Println(a) // 输出 1 ``` 从一个已经关闭但是为空的通道取值 ```go c := make(chan int, 1) close(c) a := <-c fmt.Println(a) // 输出 0 ``` 关闭一个已经关闭的通道 ```go c := make(chan int, 1) close(c) close(c) // 报错: panic: close of closed channel ``` ## 单向通道 ### 说明 在函数中使用通道时我们可以限制其为只读通道或者只写通道。 ### 只读通道 ```go func doWork(i <-chan int) { <-i // 只能取值 //i <- 1 // 存值操作将不被允许 } ``` ### 只写通道 ```go func doWork(i chan <- int) { i <- 1 // 只能存值 //<-i // 取值操作将不被允许 } ``` ## 案例 ### channel通信 使用 channel 实现通信 ```go package main import ( "fmt" "sync" "time" ) var wg sync.WaitGroup func main() { fmt.Println("嗨客网(www.haicoder.net)") c := make(chan int, 1) c <- 1 // 因为我们定义的通道带有一个缓冲, // 所以在发布任务之前允许存在最多一个任务投递,也可以写在发布任务之后 // for循环用于发布任务 for i := 0; i < 2; i++ { wg.Add(1) go doWork(c) // 如果通道现在是空的 goroutine会等待, // 如果检测到现在是空的且没有任务投递,就会报错 } c <- 2 // 多余的任务投递必须在任务发布之后 wg.Wait() } func doWork(i chan int) { defer wg.Done() a := <-i fmt.Printf("%d号开始工作了\n", a) time.Sleep(time.Second * 2) // 模拟耗时 fmt.Printf("%d号结束工作了,输出:%d\n", a, a) } ``` 此时,我们运行程序,控制台输出如下: ![www.haicoder.net](https://haicoder.net/uploads/pic/notes/golang/31_Go%E8%AF%AD%E8%A8%80channel%E4%BD%BF%E7%94%A8.png) 我们使用了 channel 实现了,收发数据。 ### channel通信 使用 channel 实现通信 ```go package main import ( "fmt" "sync" "time" ) var wg sync.WaitGroup func main() { fmt.Println("嗨客网(www.haicoder.net)") c := make(chan int, 1) wg.Add(1) go doWork(c) <-c // 取值必须在放值之后 wg.Wait() } func doWork(i chan int) { defer wg.Done() fmt.Printf("goroutine开始工作了\n", ) time.Sleep(time.Second * 2) // 模拟耗时 i <- 1 fmt.Printf("goroutine结束工作了,放入:%d\n", 1) } ``` 此时,我们运行程序,控制台输出如下: ![](https://haicoder.net/uploads/pic/notes/golang/32_Go%E8%AF%AD%E8%A8%80channel%E4%BD%BF%E7%94%A8.png) 我们使用了 channel 实现了,收发数据。 ## Golang channel使用总结 在 golang 中 channel 用于 goroutine 之间的通信,channel 是引用类型,需要使用 make 来创建 channel。在 Golang 中,用通信来共享内存,而不要用共享内存来通信。 原文地址:**[嗨客网](https://haicoder.net/note/golang/golang-channel-usage.html)** 更多文章,可以关注下方公众号: ![嗨客网](https://haicoder.net/quickmark.png)

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

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

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