Go进阶02 channel

2017-08-24 11:21:33

在谈channel之前,我们先说下并发通信,在实践中,有两种最常见的并发通信模型:共享数据和消息,被共享的数据可能有多种形式,比如内存数据块、磁盘文件、网络数据等,一般内存共享最为常见.

Go语言选择消息机制作为通信方式.消息机制认为每个并发单元是自包含的、独立的个体,并且都有自己的变量,但在不同并发单元间这些变量不共享。每个并发单元的输入和输出只有一种,那就是消息。

Go语言提供的消息通信机制就被称为channel,channel是进程内的通信方式,golang channel 分为有缓冲与无缓冲两种类型,它们最大的区别是阻塞问题.不带缓冲的channel兼具通信和同步两种特性

  • channel定义和操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    //一般channel声明
    var chanName chan ElementType
    var ch chan int
    var m map[string] chan bool

    //定义一个channel
    c := make(chan int)

    //定义一个带缓冲的channel
    c := make(chan int, 1024)

    //向一个channel发送一个值
    c <- x

    //从channel中接收一个值
    <- c

    //从channel c接收一个值并存储到x中
    x = <-c

    //从channel接收一个值,如果channel关闭或者没数据,ok将为false
    x, ok = <- c
  • 生产者消费者问题

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    package main

    import (
    "fmt"
    "time"
    )

    func producer(c chan int, max int) {
    for i :=0; i< max;i++ {
    c <- i
    }
    }
    func consumer(c chan int) {
    ok := true
    for ok {
    if value, ok := <-c; ok {
    fmt.Println(value)
    }
    }
    }

    func main() {
    c := make(chan int)
    defer close(c)
    go producer(c, 10)
    go consumer(c)
    time.Sleep(time.Millisecond * 10)
    }

    执行结构将依次打印0-9

  • 定时器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    package main

    import (
    "fmt"
    "time"
    )

    var mych = make(chan bool)

    func task() {
    fmt.Println("my timer task..")
    }

    func timer() {
    timeout := time.NewTicker(time.Millisecond * 1000)
    select {
    case <- mych:
    go task()
    case <-timeout.C:
    fmt.Println("Time out")
    }
    fmt.Println("Hello go")
    }

    func main() {
    timer()
    time.Sleep(time.Millisecond * 10)
    }

    这里没有向mychannel中写数据的,在等10秒之后会打印出hello go,程序并没有因此阻塞在这里

  • goroutine通信

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    package main
    import (
    "fmt"
    "time"
    )

    var mych1 = make(chan int)

    func routine1() {
    for i:=0; i< 10; i++ {
    mych1 <- i
    }
    }

    func routine2() {
    for {
    i := <-mych1
    fmt.Println("routine2: ", i)
    }
    }

    func routine3() {
    for {
    i := <-mych1
    fmt.Println("routine3: ", i)
    }
    }

    func main() {
    go routine1()
    go routine2()
    go routine3()
    time.Sleep(time.Millisecond * 100)
    }

    这里实现了一个goroutine写,两个goroutine读的过程,可以看到按顺序写进去的数据,被哪个goroutine读到是完全随机的,在golang中我们要实现进程间通信,channel是唯一途径,也是推荐的途径,它的底层是通过共享内存实现的

  • 总结
    channel定义和操作
    生产者和消费者
    goroutine通信,在使用channel的时候,至少有一个goroutine来负责读,否则你的程序就会阻塞在你写channel的地方


您的鼓励是我写作最大的动力

俗话说,投资效率是最好的投资。 如果您感觉我的文章质量不错,读后收获很大,预计能为您提高 10% 的工作效率,不妨小额捐助我一下,让我有动力继续写出更多好文章。