golang如何更好地使用channel

添加时间:2020-05-08 10:49:46

来源:

浏览:

最近学习了《GO语言并发之道》这本书,获益匪浅,其中channel2021最火现金棋牌手机面的知识了解了更多,主要是以下几点:

1. channel在不同条件下读写,会有不同的行为形式,后面会通过实验说明;

2. channel使用完是要close的,而一般由写端创建和关闭,不要在读端关闭,上面的实验结果会说明这样做的原因;

3. channel结合gorouting有很多的实践2021最火现金棋牌手机式,还可以构造流式处理。


先来看看实验代码,首先看看读channel的代码

package read

 

import "fmt"

 

// 阻塞

func Nil() {

var ch chan interface{}

<-ch

}

 

// 阻塞

func Empty() {

ch := make(chan interface{})

<-ch

}

 

// 阻塞

func CloseNotEmpty() {

ch := make(chan interface{})

ch <- 10

close(ch)

fmt.Println(<-ch)

}

 

// 读取0值

func CloseEmpty() {

ch := make(chan interface{})

close(ch)

fmt.Println(<-ch)

}


函数主要包括如下内容,实验结果已经在代码中进行了注释:

Nil:读取nil channel

Empty:读取没有数据的channel

CloseNotEmpty:读取被关闭的非空channel

CloseEmpty:读取被关闭的空channel


然后是写channel的代码

package write

 

// 阻塞

func Nil() {

var ch chan interface{}

ch <- 10

}

 

// 阻塞

func Full() {

ch := make(chan interface{}, 1)

ch <- 10

ch <- 11

}

 

// 可写入

func NotFull() {

ch := make(chan interface{}, 1)

ch <- 10

}

 

// panic send on closed channel

func Closed() {

ch := make(chan interface{}, 1)

close(ch)

ch <- 10

}


函数说明如下,实验结果也已经在注释中给出:

Nil:向nil channel写入数据

Full:向填满的channel写入数据

NotFull:向未填满的channel写入数据

Closed:向已经关闭的channel写入数据

最后是实验关闭channel的函数:

package close

 

//panic: close of nil channel

func Nil() {

var ch chan interface{}

close(ch)

}

 

//panic: close of closed channel

func Closed() {

ch := make(chan interface{})

close(ch)

close(ch)

}


函数说明如下,结果注释已给出:

Nil:关闭nil channel

Closed:关闭已经关闭的channel


从上面的实验结果可以看出,如果从读端关闭channel,写端去写关闭的channel时,会出现panic,导致整个程序终止。而从写端关闭channel,读取时,返回0值或阻塞,至少有迹可循,2021最火现金棋牌手机便2021最火现金棋牌手机调试代码。


下面给出了结合channel如何编写比较好的gorouting的一个例子

package main

 

import (

"fmt"

"strings"

"time"

)

 

func toUpper(done <-chan interface{}, str string) <-chan string {

strChan := make(chan string)

 

go func() {

defer close(strChan)

 

for {

select {

case <-done:

return

case strChan <- strings.ToUpper(str):

}

}

}()

 

return strChan

}

 

func main() {

done := make(chan interface{})

defer close(done)

 

toUpperChan := toUpper(done, "aaBBcc")

fmt.Println(<-toUpperChan)

}


toUpper函数接受两个参数,第一个参数是一个可读channel,用来终止协程,str是需要转换为大写的字符串。返回值是一个可读的channel,这个channel作为协程返回数据的媒介,如果用过Java,也可以理解为这个channel类似于Future对象,也可以理解为是协程的join点。toUpper函数逻辑比较简单,一个比较大的select,读取done channel并向strChan写入大写转换后的字符串。遵循上面提到的原则,作为strChan的写端,toUpper函数创建strChan,并通过defer延迟执行,在函数return后,关闭strChan。


主函数创建done channel并传递给toUpper,当主函数退出时,通过关闭done channel,toUpper函数中将从done读取到nil,从而退出协程函数。主函数调用toUpper函数,通过返回的channel,读取处理后的字符串。


可以看到,采用这种2021最火现金棋牌手机式,使得协程的管理更加简单,还能保证channel正常关闭,取得协程返回值,避免了channel和gorouting的泄露。下面这段代码,更加华丽

package main

 

import (

"fmt"

"strings"

"time"

)

 

func str(done <-chan interface{}, str string) <-chan string {

strChan := make(chan string)

 

go func() {

time.Sleep(2 * time.Second)

 

defer close(strChan)

 

select {

case <-done:

return

case strChan <- str:

}

}()

 

return strChan

}

 

func toUpper(done <-chan interface{}, str <-chan string) <-chan string {

strChan := make(chan string)

 

go func() {

time.Sleep(2 * time.Second)

 

defer close(strChan)

 

for {

select {

case <-done:

return

case strChan <- strings.ToUpper(<-str):

}

}

}()

 

return strChan

}

 

func appendStr(done <-chan interface{}, oldStr <-chan string, appendStr string) <-chan string {

strChan := make(chan string)

 

go func() {

time.Sleep(2 * time.Second)

 

defer close(strChan)

 

for {

select {

case <-done:

return

case strChan <- <-oldStr + appendStr:

}

}

}()

 

return strChan

}

 

func main() {

done := make(chan interface{})

 

strChan := str(done, "aaa111bbb222CCC")

fmt.Println(1)

 

upperChan := toUpper(done, strChan)

fmt.Println(2)

 

appendStrChan := appendStr(done, upperChan, "dddd")

fmt.Println(3)

 

fmt.Println(<-appendStrChan)

 

close(done)

 

done = make(chan interface{})

fmt.Println(<-appendStr(done, toUpper(done, str(done, "aaa111bbb222CCC")), "dddd"))

close(done)

}


有三个函数,str,toUpper和appendStr,这三个函数都采用了上面的模式,不同的是toUpper和appendStr这两个函数的第二个输入参数使用了一个可读channel,这样,2021最火现金棋牌手机可以构造一个异步的流式处理流程,str作为这个流处理的入口,参数使用原生类型,toUpper和appendStr作为流处理的处理节点,使用通道作为参数。


main函数中给出了两种组装流处理流程的2021最火现金棋牌手机式,第一种2021最火现金棋牌手机式,通过调用每一个函数,然后将返回的channel作为下一个处理节点输入进行传参,中间添加了打印,运行时可以看到,函数调用是立刻返回的,在最后获取结果时进行等待;第二种2021最火现金棋牌手机式更加简洁,基于函数式编程构造了流处理。


关闭done channel可以起到广播的作用,所有等待在读取done的协程,都会读到nil,从而保证流处理过程中使用的所有协程都能退出。


golang的高并发主要靠channel和gorouting实现,深刻理解两者的使用2021最火现金棋牌手机式,将有利于2021最火现金棋牌手机写出更高效的golang代码。

上一篇插件式架构设计
下一篇

Golang  相关内容

——
08

2020-05

golang如何更好地使用channel

golang如何更好地使用channel… [了解更多]

30

2020-04

Golang之Go Module的使用

go module使用入门… [了解更多]

2021最火现金棋牌手机

地 址:太原市晋阳街慧谷产业园三层D区

邮政编码:030006

电 话:0351-7773556

传 真:0351-7773556

邮 箱:fs-tec@towantto.com