go中的chan管道机制

2024-08-26 16:11:14 浏览数 (2)

前言

在 Go 语言中,提倡通过通信来共享内存,而不是通过共享内存来通信,go中的Channel(一般简写为 chan) 管道提供了一种机制,它在两个并发执行的协程之间进行同步,并通过传递与该管道元素类型相符的值来进行通信,可以用来两个不同的协程之间共享数据

chan使用

chan类型

channel是一种类型,一种引用类型,声明类型时,可以使用

代码语言:javascript复制
go 代码解读复制代码var chan2 = make(chan int)

或者

代码语言:javascript复制
go 代码解读复制代码var chan2 = make(chan int64)

等等,创建chan用make实现,并且channel遵循先进先出原则

chan使用

chan在两个不同的协程之间通讯

代码语言:javascript复制
go 代码解读复制代码package main

import (
    "fmt"
    _ "fmt"
    "time"
)

func main() {
    var chan2 = make(chan int)
    go say(chan2)
    go say1(chan2)
    time.Sleep(5 * time.Second)
}

func say(a chan<- int) {
    for i := 0; i < 100; i   {
       a <- i
    }
}

func say1(a <-chan int) {
    for i := 0; i < 100; i   {
       data := <-a
       fmt.Println(data)
    }
}
chan使用注意

chan分为无缓存 channel 和有缓存 channel,例如

有缓存 channel
代码语言:javascript复制
go 代码解读复制代码package main

import (
    _ "fmt"
    "log"
)

func main() {
    var chan2 = make(chan int)
    chan2 <- 1
    data := <-chan2
    log.Println("data的值为:", data)
}

以上输出结果为

image.pngimage.png
image.pngimage.png

是因为无缓存线写之后,会阻塞

有缓存channel
代码语言:javascript复制
go 代码解读复制代码package main

import (
    _ "fmt"
    "log"
)

func main() {
    var chan2 = make(chan int, 1)
    chan2 <- 1
    data := <-chan2
    log.Println("data的值为:", data)
}

以上输出结果为

image.pngimage.png

但是超过定义的缓存,就会发生死锁

代码语言:javascript复制
go 代码解读复制代码package main

import (
    _ "fmt"
    "log"
)

func main() {
    var chan2 = make(chan int, 1)
    chan2 <- 1
    chan2 <- 2
    data := <-chan2
    log.Println("data的值为:", data)
}

以上输出结果为

image.pngimage.png
从chan取值

使用range可以从channel取值。如

代码语言:javascript复制
go 代码解读复制代码package main

import "log"

func main() {
    ch := make(chan int64)
    go say(ch)
    for i := range ch {
       data := i
       log.Println(data)
    }
}
func say(ch chan int64) {
    for i := 0; i < 100; i   {
       ch <- int64(i)
    }
    close(ch)
}

以上结果为

image.pngimage.png

但是要注意的是,在使用range遍历时,需要关闭管道,否则会报死锁

代码语言:javascript复制
go 代码解读复制代码package main

import "log"

func main() {
    ch := make(chan int64)
    go say(ch)
    for i := range ch {
       data := i
       log.Println(data)
    }
}
func say(ch chan int64) {
    for i := 0; i < 100; i   {
       ch <- int64(i)
    }
    //close(ch)
}

以上输出结果为

image.pngimage.png

channel只读没写,也会报死锁问题

代码语言:javascript复制
go 代码解读复制代码package main

import "fmt"

func main() {
    ch := make(chan int64)
    data := <-ch
    fmt.Println(data)
}

以上结果为

image.pngimage.png

使用切片的channel就不会报死锁

代码语言:javascript复制
go 代码解读复制代码package main

import (
    "fmt"
    "time"
)

func main() {
    channels := make([]chan int, 2)
    for i := 0; i < 2; i   {
       go func(ch chan int) {
          time.Sleep(time.Second)
          ch <- 1
       }(channels[i])
    }

    for ch := range channels {
       fmt.Println("执行结果为:", ch)
    }
    fmt.Println("执行结束=====================")
}
chnnel可读可写

channel 可以分为 3 种类型:

  • 只读 channel,单向 channel
  • 只写 channel,单向 channel
  • 可读可写 channel 默认情况下,都是可读可写的,如
代码语言:javascript复制
go 代码解读复制代码ch := make(chan int64)

定义一个可读管道

代码语言:javascript复制
css 代码解读复制代码func say(ch <-chan int) {
    for i := 0; i < 100; i   {
       data := <-ch
       log.Println("结果为:", data)
    }
}

定义一个只可写通道

代码语言:javascript复制
css 代码解读复制代码func say1(ch chan<- int) {
    for i := 0; i < 100; i   {
       ch <- i
    }
}

使用如下

代码语言:javascript复制
go 代码解读复制代码package main

import (
    "log"
    "time"
)

func main() {
    var ch = make(chan int)
    go say(ch)
    go say1(ch)
    time.Sleep(5 * time.Second)
}

func say(ch <-chan int) {
    for i := 0; i < 100; i   {
       data := <-ch
       log.Println("结果为:", data)
    }
}

func say1(ch chan<- int) {
    for i := 0; i < 100; i   {
       ch <- i
    }
}
chan超时

chan配合select机制可以设置阻塞超时

代码语言:javascript复制
go 代码解读复制代码package main

import (
    "fmt"
    "time"
)

func main() {
    var ch = make(chan int)
    go say(ch)
    select {
    case data, ok := <-ch:
       fmt.Println(data, ok)
    case <-time.After(3 * time.Second):
       fmt.Println("================超时")
    }
}

func say(ch chan int) {
    time.Sleep(5 * time.Second)
    ch <- 1
    fmt.Println("==============执行")
}
image.pngimage.png

总结

合理的使用channel,在并发中更好的进行写成之间的通讯

0 人点赞