golang基础--Goroutine与Channel

发布时间 2023-05-06 18:28:44作者: 99号的格调

什么是goroutine?

  goroutine是go特有的并发体,是一种轻量级的线程,由go关键字启动。goroutineGo语言提供的一种用户态线程,有时我们也称之为 协程。所谓的协程,某种程度上也可以叫做轻量线程,它不由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程序结束")
}