前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >go channel 管道

go channel 管道

作者头像
看、未来
发布于 2022-06-19 05:32:44
发布于 2022-06-19 05:32:44
69400
代码可运行
举报
运行总次数:0
代码可运行

文章目录

概述

协程是并发编程的基础,而管道(channel)则是并发中协程之间沟通的桥梁,很多时候我们启动一个协程去执行完一个操作,执行操作之后我们需要返回结果,或者多个协程之间需要相互协作。


channel 基本使用

创建 channel

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var ch0 chan int
var ch1 chan string
var ch2 chan map[string]string
type stu struct{}
var ch3 chan stu
var ch4 chan *stu

channel 方向 <-

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ch <- v    // 发送值v到Channel ch中
v := <-ch  // 从Channel ch中接收数据,并将数据赋值给v

它包括三种类型的定义。可选的<-代表channel的方向。如果没有指定方向,那么Channel就是双向的,既可以接收数据,也可以发送数据。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
chan T          // 可以接收和发送类型为 T 的数据
chan<- float64  // 只可以用来发送 float64 类型的数据
<-chan int      // 只可以用来接收 int 类型的数据

两个方向容易记混,这样,就默认我们是没有被显式写出的,那么 chan<- meme <-chan 就好记了吧。

在通讯(communication)开始前channel和expression必选先求值出来(evaluated),比如下面的(3+4)先计算出7然后再发送给channel。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
c := make(chan int)
defer close(c)
go func() { c <- 3 + 4 }()
i := <-c
fmt.Println(i)

send被执行前(proceed)通讯(communication)一直被阻塞着。如前所言,无缓存的channel只有在receiver准备好后send才被执行。如果有缓存,并且缓存未满,则send会被执行往一个已经被close的channel中继续发送数据会导致run-time panic。往nil channel中发送数据会一致被阻塞着。

<-ch用来从channel ch中接收数据,这个表达式会一直被block,直到有数据可以接收。 从一个nil channel中接收数据会一直被block。

从一个被close的channel中接收数据不会被阻塞,而是立即返回,接收完已发送的数据后会返回元素类型的零值(zero value)。

如前所述,你可以使用一个额外的返回参数来检查channel是否关闭。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
x, ok := <-ch
x, ok = <-ch
var x, ok = <-ch

如果OK 是false,表明接收的x是产生的零值,这个channel被关闭了或者为空。


make进行初始化

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var ch0 chan int =make(chan int)
var ch1 chan int =make(chan int,10)

通过缓存的使用,可以尽量避免阻塞,提供应用的性能。 容量(capacity)代表Channel容纳的最多的元素的数量,代表Channel的缓存的大小。 如果没有设置容量,或者容量设置为0, 说明Channel没有缓存,只有sender和receiver都准备好了后它们的通讯(communication)才会发生(Blocking)。如果设置了缓存,就有可能不发生阻塞, 只有buffer满了后 send才会阻塞, 而只有缓存空了后receive才会阻塞。一个nil channel不会通信。 无缓存的与有缓存channel有着重大差别,那就是一个是同步的 一个是非同步的。

你可以在多个goroutine从/往 一个channel 中 receive/send 数据, 不必考虑额外的同步措施。


关闭 chan

这个要提一下,因为昨天写那个 “生成消费者” 就因为关闭出问题了。

使用内置函数close进行关闭,chan关闭之后,for range遍历chan中已经存在的元素后结束。 使用内置函数close进行关闭,chan关闭之后,没有使用for range的写法,需要使用,v, ok := <- ch进行判断chan是否关闭。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import "fmt"

func main() {

	var ch chan int
	ch = make(chan int, 5)
	for i := 0; i < 5; i++ {
		ch <- i
	}
	close(ch)
	for {
		var b int
		b, ok := <-ch
		if ok == false {
			fmt.Println("chan is close")
			break
		}
		fmt.Println(b)
	}
}

这个是很正常的。

如果将close(ch)注释掉,意思是不关闭管道,那么会出现dead lock死锁。因为存入管道5个数字,然后无限取数据,会出现死锁。

向 closed channel 发送数据引发 panic 错误,接收立即返回零值。而 nil channel, 无论收发都会被阻塞。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

func main() {
	ch := make(chan int, 1)
	close(ch)
	ch <- 2
}

range 遍历 chan

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import "fmt"

func main() {
	var ch chan int
	ch = make(chan int, 10)
	for i := 0; i < 10; i++ {
		ch <- i
	}
	close(ch)
	for v := range ch {
		fmt.Println(v)
	}
}

同样如果将close(ch)注释掉,意思是不关闭管道,那么会出现dead lock死锁。


内置函数len()、cap()

len 返回未被读取的缓冲元素数量,cap 返回缓冲区大小。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import "fmt"

func main() {
	ch1 := make(chan int)
	ch2 := make(chan int, 3)
	ch2 <- 1
	fmt.Println(len(ch1), cap(ch1))
	fmt.Println(len(ch2), cap(ch2))
}

select

select 语句类似于 switch 语句,但是select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。 select语句选择一组可能的send操作和receive操作去处理。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import (
	"fmt"
)

func main() {
	ch1 := make(chan int, 1)
	ch1 <- 1
	ch2 := make(chan int, 1)
	ch2 <- 2
	select { //随机读数
	case k1 := <-ch1:
		fmt.Println(k1)
	case k2 := <-ch2:
		fmt.Println(k2)
	default:
		fmt.Println("chan")
	}
}

timeout

select有很重要的一个应用就是超时处理。 因为上面我们提到,如果没有case需要处理,select语句就会一直阻塞着。这时候我们可能就需要一个超时操作,用来处理超时的情况。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import (
	"fmt"
	"time"
)

func main() {
	c1 := make(chan string, 1)
	go func() {
		time.Sleep(time.Second * 2)
		c1 <- "result 1"
	}()
	select {
	case res := <-c1:
		fmt.Println(res)
	case <-time.After(time.Second * 1):
		fmt.Println("timeout 1")
	}
}

channel单向

可以将 channel 隐式转换为单向队列,只收或只发。 不能将单向 channel 转换为普通 channel。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import (
	"fmt"
)

func main() {
	c := make(chan int, 3)
	var send chan<- int = c // send-only
	var recv <-chan int = c // receive-only
	send <- 5               // <-send               // Error: receive from send-only type chan<- int
	val, ok := <-recv
	if ok {
		fmt.Println(val)
	} // recv <- 2           // Error: send to receive-only type <-chan int
}

可以试着将 send 和 recv 的顺序反过来看看。


chan 作为 chan数

channel 是第一类对象,可传参 (内部实现为指针) 或者作为结构成员。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import "fmt"

type Request struct {
	data []int
	ret  chan int
}

func NewRequest(data ...int) *Request {
	return &Request{data, make(chan int, 1)}
}

func Process(req *Request) {
	x := 0
	for _, i := range req.data {
		x += i
	}
	req.ret <- x
}
func main() {
	req := NewRequest(10, 20, 30)
	Process(req)
	fmt.Println(<-req.ret)
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-06-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Go基础系列:channel入门
channel用于goroutines之间的通信,让它们之间可以进行数据交换。像管道一样,一个goroutine_A向channel_A中放数据,另一个goroutine_B从channel_A取数据。
李海彬
2018/12/18
7650
Go基础系列:channel入门
go channel 使用及机制流程汇总
makechan()初始化hchan结构体, 如果没有缓冲区即分配hchanSize大小的内存并返回;而有缓冲区的情况下, 则计算管道元素类型大小并分配hchanSize+(elem.size * size)大小的内存(缓冲区是一个环形的结构设计), 最后返回hchan.
会呼吸的Coder
2020/02/17
4340
由浅入深聊聊Golang中select的实现机制
select是go语言中常用的一个关键字,其用法也一直被用作面试题来考核应聘者。今天,结合代码来分析下select的主要用法。
会呼吸的Coder
2020/02/17
1.5K0
【Go】留意 Select 的预求值!
关键在于 recvAndSend 函数的 case ch1 <- <-ch2: 我们希望在一条 case 中从 chan2 中取出数据并放到 chan1 中,但事实上这样会导致死锁,虽然平时谁也不会写出这种神仙代码,但下面这个就很容易被写出来了:
JuneBao
2022/10/26
2120
《快学 Go 语言》第 12 课 —— 神秘的地下通道
不同的并行协程之间交流的方式有两种,一种是通过共享变量,另一种是通过队列。Go 语言鼓励使用队列的形式来交流,它单独为协程之间的队列数据交流定制了特殊的语法 —— 通道。
老钱
2018/12/24
4080
《快学 Go 语言》第 12 课 —— 神秘的地下通道
Golang之旅23-通道channel
在goroutine并发执行的时候,需要在函数和函数之间进行通信。Go语言并发模式CSP(communicating Sequents Processes),通过通信共享内存。
皮大大
2021/03/02
3430
学会 Go select 语句,轻松实现高效并发
在 Go 语言中,Goroutine 和 Channel 是非常重要的并发编程概念,它们可以帮助我们解决并发编程中的各种问题。关于它们的基本概念和用法,前面的文章 一文初探 Goroutine 与 channel 中已经进行了介绍。而本文将重点介绍 select,它是协调多个 channel 的桥梁。
陈明勇
2023/10/15
8200
学会 Go select 语句,轻松实现高效并发
Golang 基础之基础语法梳理 (二)
单纯地将函数并发执行是没有意义的。函数与函数间需要交换数据才能体现并发执行函数的意义,channel就是它们之间的连接。
帽儿山的枪手
2022/03/20
6960
Golang 基础之基础语法梳理 (二)
go语言的成神之路-筑基篇-管道
https://cloud.tencent.com/developer/article/2466159?shareByChannel=link
用户11367190
2024/12/02
850
Go Channel 详解
Channel是Go中的一个核心类型,你可以把它看成一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯(communication)。
李海彬
2018/07/26
1.2K0
10.Go-goroutine,waitgroup,互斥锁,channel和select
互斥锁表示锁的代码同一时间只能有一个goroutine运行,而读写锁表示在锁范围内数据的读写操作
zhang_derek
2019/08/12
7870
大道如青天,协程来通信,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang通道channel的使用EP14
    众所周知,Go lang的作用域相对严格,数据之间的通信往往要依靠参数的传递,但如果想在多个协程任务中间做数据通信,就需要通道(channel)的参与,我们可以把数据封装成一个对象,然后把这个对象的指针传入某个通道变量中,另外一个协程从这个通道中读出变量的指针,并处理其指向的内存对象。
用户9127725
2022/09/23
2050
大道如青天,协程来通信,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang通道channel的使用EP14
Golang中的管道(channel) 、goroutine与channel实现并发、单向管道、select多路复用以及goroutine panic处理
管道(channel)是 Go 语言中实现并发的一种方式,它可以在多个 goroutine 之间进行通信和数据交换。管道可以看做是一个队列,通过它可以进行先进先出的数据传输,支持并发的读和写。
周小末天天开心
2023/10/16
7040
Golang中的管道(channel) 、goroutine与channel实现并发、单向管道、select多路复用以及goroutine panic处理
深入学习Golang—channel
Channel 1. 概述 “网络,并发”是Go语言的两大feature。Go语言号称“互联网的C语言”,与使用传统的C语言相比,写一个Server所使用的代码更少,也更简单。写一个Server除了网络,另外就是并发,相对python等其它语言,Go对并发支持使得它有更好的性能。 Goroutine和channel是Go在“并发”方面两个核心feature。 Channel是goroutine之间进行通信的一种方式,它与Unix中的管道类似。 Channel声明: ChannelType = ( "chan
李海彬
2018/03/21
1.3K0
Go语言中的Channel:打开并发编程的神秘之门
并发编程是现代编程语言的重要组成部分,Go语言通过goroutines和channel实现了高效的并发编程机制。
windealli
2024/04/19
3880
Go语言中的Channel:打开并发编程的神秘之门
如何使用 Go 更好地开发并发程序,纯干货!
Go 语言的并发特性是其一大亮点,今天我们来带着大家一起看看如何使用 Go 更好地开发并发程序?
aoho求索
2021/03/16
5420
如何使用 Go 更好地开发并发程序,纯干货!
Go基础--goroutine和channel
goroutine 在go语言中,每一个并发的执行单元叫做一个goroutine 这里说到并发,所以先解释一下并发和并行的概念: 并发:逻辑上具备同时处理多个任务的能力 并行:物理上在同一时刻执行多个并发任务 当一个程序启动时,其主函数即在一个单独的goroutine中运行,一般这个goroutine是主goroutine 如果想要创建新的goroutine,只需要再执行普通函数或者方法的的前面加上关键字go 通过下面一个例子演示并发的效果,主goroutine会计算第45个斐波那契函数,在计算的同时会循环
coders
2018/03/30
7930
Go基础--goroutine和channel
Go 语言并发编程系列(六)—— 通道类型篇:单向通道及其使用
我们介绍了管道类型的基本语法,通常,管道都是支持双向操作的:既可以往管道发送数据,也可以从管道接收数据。但在某些场景下,可能我们需要限制只能往管道发送数据,或者只能从管道接收数据,这个时候,就需要用到单向通道。
学院君
2019/08/29
1.8K0
「一闻秒懂」你了解goroutine和channel吗?
大家都知道进程是操作系统资源分配的基本单位,有独立的内存空间,线程可以共享同一个进程的内存空间,所以线程相对轻量,上下文切换开销也小。虽然线程已经比较轻量了,但还是占近1M的内存,而今天介绍的有“轻量级线程”之称的Goroutine,可以小至几十K甚至几K,切换的开销更小。
平也
2020/04/16
5120
「一闻秒懂」你了解goroutine和channel吗?
A Bite of GoLang(下)
8. Goroutine 8.0、Goroutine介绍 协程 Coroutine 轻量级"线程" 上面的两个特征到底是什么意思呢?下面我们通过具体的事例详细的讲述一下, package main
盛国存
2018/05/14
1.1K1
A Bite of GoLang(下)
推荐阅读
相关推荐
Go基础系列:channel入门
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档