golang基础--Goroutine与Channel
什么是goroutine?
goroutine是go特有的并发体,是一种轻量级的线程,由go关键字启动。goroutine是Go语言提供的一种用户态线程,有时我们也称之为 协程。所谓的协程,某种程度上也可以叫做轻量线程,它不由os,而由应用程序创建和管理,因此使用 开销较低(一般为4K)。我们可以创建很多的goroutine,并且它们跑在同一个内核线程之上的时 候,就需要一个调度器来维护这些goroutine,确保所有的goroutine都使用cpu,并且是尽可能公平的使用cpu资源。
什么是Channel?
Channel是goroutine之间互相通信的通道,goroutine可以通过它来发送消息和接收消息。
通常,通过make来初始化一个channel
channel_test1 := make(chan int)
当通道变量创建好后,即可以使用该变量进行数据的发送,接收和关闭。数据的发送和接收均适用“<-”符号,关闭则使用close函数。
注意:一但通道关闭,就无法通过这个通道发送数据了,但不会影响数据的接收,直到通道中的数据队列为空。此时,若尝试再次获取数据,将返回对应类型的默认值。另外,close函数无法关闭一个已经关闭的channel。
无缓冲的channel
无缓冲channel只有在等待接收数据的时候,才能成功发送数据。
示例代码:
package main import ( "fmt" "reflect" "time" ) func IntChanRecvListener(intChan chan int) { intValue := <-intChan fmt.Println(intValue, reflect.TypeOf(intValue)) } func main() { var intChan = make(chan int) fmt.Println(intChan, reflect.TypeOf(intChan)) go IntChanRecvListener(intChan) intChan <- 100 close(intChan) time.Sleep(time.Second) }
无缓冲的channel要求数据的发送和接收必须成对出现,必须先有数据的接收者,且数据的发送和接收必须同步。
带缓冲的Channel
再使用make穿件channel变量时,可以通过定义缓冲区大小来构建带缓冲的channel。带缓冲的channel不要求必须有数据的接收者才能成功发送数据。使用内置函数len获取channel中元素的数量,cap获取channel缓冲区的大小。
示例代码:
package main import ( "fmt" "time" ) func IntChanRecListener(intChan chan int) { intValue := <-intChan //fmt.Println(intValue, reflect.TypeOf(intValue)) fmt.Println("成功接收第第1个value,通道元素个数,当前缓冲区大小:", intValue, len(intChan), cap(intChan)) intValue = <-intChan //fmt.Println(intValue, reflect.TypeOf(intValue)) fmt.Println("成功接收第第2个value,通道元素个数,当前缓冲区大小:", intValue, len(intChan), cap(intChan)) } func main() { var intChan2 = make(chan int, 2) intChan2 <- 100 fmt.Println(len(intChan2), cap(intChan2)) intChan2 <- 200 fmt.Println(len(intChan2), cap(intChan2)) go IntChanRecListener(intChan2) time.Sleep(time.Second * 2) close(intChan2) }
判断Channel是否关闭
再使用channel读取数据时,可以获得两个返回值:一个是获取的值,一个是布尔值,当channel关闭后,为false,否则为true
示例代码:
package main import "fmt" //判断channel是否关闭 func main() { initchan := make(chan int, 10) initchan <- 1 initchan <- 2 initchan <- 3 close(initchan) for { initvalue, isOpen := <-initchan if !isOpen { fmt.Println("channel 已经关闭") break } fmt.Println(initvalue) } }
Select结构
select结构类似switch--case结构,也由若干个分支及一个默认分枝构成,每个case分支对应一条channel的数据收发操作。select结构会同时监听一个或多个channel,简化处理多个channel的流程。
示例代码:
package main //select 的用法:select结构会同时监听一个或多个通道,简化处理多个通道的流程。 import ( "fmt" "time" ) func sendFunc1(chan1 chan int) { for i := 0; i < 5; i++ { chan1 <- i time.Sleep(1 * time.Second) } } func sendFunc2(chan2 chan int) { for i := 10; i >= 5; i-- { chan2 <- i time.Sleep(1 * time.Second) } } func recvFunc(chan1 chan int, chan2 chan int) { for { select { case intvalue1 := <-chan1: fmt.Println("接收到chan1通道的值:", intvalue1) case intvalue2 := <-chan2: fmt.Println("接收到chan2通道的值:", intvalue2) } } } func main() { chan1 := make(chan int, 5) chan2 := make(chan int, 5) go recvFunc(chan1, chan2) go sendFunc1(chan1) go sendFunc2(chan2) time.Sleep(time.Second * 5) fmt.Println("main程序结束") }