Go-goroutine和通道

goroutine

通信顺序进程CSP,是一个并发的模式,在不同的执行体(goroutine)之间传递值,但是变量本身局限于单一的执行体。

每一个并发执行的活动称为goroutine;

当一个程序启动时,只有一个goroutine来调用main函数,称为主gourounie;

新的goroutine通过go语句创建。语法上,在普通函数或方法调用前加上go关键字;

f() //调用f(),等待返回
go f() //新建一个调用f()的goroutine,不用等待

main函数返回,所有的goroutine直接终结,程序退出;

没有程序方法让一个gorounie来停止另一个;

有办法和goroutine通信来要求它自己停止;

func main(){
    listenser, err := net.Listen("tcp", "localhost:8080")
    if err != nil{
        log.Fatal(err)
    }

    for{
        conn, err := linstenser.Accept()
        if err != nil{
            log.Println(err) //连接终止
            continue
        }

        go handleConn(conn) //并发处理
    }
}

channel

groroutine是并发的执行体,通道是他们之间的连接;

通道是让一个goroutine发送特定值到另一个goroutine的通信机制;

扫描二维码关注公众号,回复: 10768703 查看本文章

每一个通道是一个具体类型的导管,叫做通道的元素类型。

一个int类型元素的通道写作 chan int.

初始化

使用内置make函数来创建通道:

ch := make(chan int) //ch的类型是chan int, 无缓冲通过
ch := make(chan int, 0) //无缓冲通道
ch := make (chan int, 3) //容量为3的缓冲通道

通道是一个使用make创建的数据结构的引用。

当复制或作为一个参数传递到一个函数,复制的是引用,调用者和被调用者都引用同一份数据结构。

通道的零值是nil.

同种类型的通道可以使用==符号比较,当二者是同一通道数据的引用时,值为true.也可以和nil比较。

通道的两个操作

send语句从一个goroutine传输一个值到另一个执行接收的goroutine.

发送(send): ch <- x //发送语句

接收(receive) : x = <- ch //赋值语句中的接收表达式

<- ch //接收语句,丢弃结果

第三个操作:close(ch)

设置一个标志位来指示当前已经发送完毕,这个通道没值了。

在一个已经关闭的通过上进行接收操作,将获取所有已发送的值,直到通过为空。

通道类型

无缓冲通道:不带缓存的channels

管道,pipeline: 串联的channels

单向通道:单方向的channel

缓冲通道:带缓存的channels

无缓冲通道

无缓冲即同步,接收值后发送方goroutine才再次唤醒。

并发:不一定同时发生,但不能确定发生的先后顺序;

串行:有一个依赖关系,发生的时间先后顺序固定;

并行:可以在同一个时间发生;

func main(){
    conn, err := net.Dial("tcp", "localhost:8080")
    checkError(err)
    done := make(chan struct{})

    go func(){
        io.Copy(os.stdout, conn)
        log.Println("done")
        done <- struct{}{} //指示主goroutine
    }()

    mustCopy(conn, os.stdin)
    conn.close()

    <- done //等待后台goroutine完成
}

管道 pipeline

counter————> square ————> printer

0,1,2,3... 0,1,4,9...

如果发送方知道没有更多数据,告诉接收者所在goroutine停止等待是有效的,close(chan)

通道关闭后,任何后续的发送操作将导致应用崩溃。

当关闭的通道被读完,所有的后续的接收操作仍可进行,只是获取到的是零值。

func main(){
    naturals := make(chan int)
    squares := make(chan int)

    //counter
    go func(){
        for x := 0; x < 100; x++{
            naturals <- x
        }

        close(naturals)
    }()



    //square
    go func(){
        for x := range natruals{
            square <- x*x
        }
        close(square)
    }()

    //printer 在主goroutine中
    for x := range squares{
        fmt.println(x)
    }
}

单向通道

chan <- int: 只能发送;

<- chan int: 只能接收;
 

func counter (out chan <- int){
    for x:= 0; i < 100; x++{
        out <- x
    }
    close(out)
}



func squarer (out chan <- int, in <- chan int){
    for v := range in{
        out <- v:v
    }
    close(out)
}



func printer (in <- chan int){
    for v := range in{
        fmt.Println(v)
    }
}



//任何赋值操作将双向通道转换为单向通道都允许的,反过来不行
func main(){
    naturals := make(chan int)
    squares := make(chan int)

    go counter(naturals)
    go squares(squares, naturals)
    printer(squares)
}

缓冲通道

一个有容量的先进先出队列;

组装流水线是对通道和goroutine合适的比喻;

如果通道已满,对于它的所有发送操作会阻塞,直到通道中所有的元素被取走。此时,通道会优先通知最早等待发送的goroutine,再次执行发送操作。

如果通道为空,对于它的接收操作会被阻塞,直到通道中有新元素出现。此时,通道会优先通知最早等待接收的goroutine,并使它再次执行接收操作。

发布了127 篇原创文章 · 获赞 24 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/Linzhongyilisha/article/details/99694828