golang channel simple use

目录

1.1 channel 基本概念相关

1.1.1 channel

1.1.2 有缓冲 channel

1.2 channel 应用

1.2.1 等待goroutine完成

1.2.2 多个goroutine协同

1.2.3 channel 和 range

1.2.4 channel 与 select

        

        写这篇文章主要参考另外一篇文章,那篇文章已经写的非常完美,我会在参考文章中写道,但里面涉及到一些基础知识,比如说,什么是channel,什么是有缓冲的channel,什么是select,如果没有这些基础知识的话,还需要到处找资料,所以计划把一些基础的go知识融合进去。

1.1 channel 基本概念相关

1.1.1 channel

通道(channel)是用来传递数据的一个数据结构。

通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。

ch <- v    // 把 v 发送到通道 ch
v := <-ch  // 从 ch 接收数据
           // 并把值赋给 v

声明一个通道很简单,我们使用chan关键字即可,通道在使用前必须先创建:

ch := make(chan int)

1.1.2 有缓冲 channel

        它的主要作用是可以实现异步,因为无缓冲的channel,实际是只有一个位置来存储数据结构,那么当它发送一个数据到channel的话,再往 channel中发送数据,程序就会阻塞掉,特别是,如果没有开goroutine去channel中取数据,你再发送数据,程序处于死锁状态,就会报错了,但是如果有缓冲,比如缓冲是2,就可以同时发送两条数据到channel,而不至于阻塞掉

       可以通过 make 的第二个参数指定缓冲区大小:

ch := make(chan int, 100)

如下小例子

package main

import "fmt"

func main() {
    // 这里我们定义了一个可以存储整数类型的带缓冲通道
        // 缓冲区大小为2
        ch := make(chan int, 2)

        // 因为 ch 是带缓冲的通道,我们可以同时发送两个数据
        // 而不用立刻需要去同步读取数据
        ch <- 1
        ch <- 2

        // 获取这两个数据
        fmt.Println(<-ch)
        fmt.Println(<-ch)

执行结果

1
2

注意到,如果不开缓冲,程序直接会死锁报错

1.2 channel 应用

    channel有点类似于管道,它在goroutine同步与通信中,有着起承转合的作用

package main

func main() {
	senderOnly := make(chan<- int)   // 只能用来发送(管道的入口,只进不出)
	receiverOnly := make(<-chan int) // 只能用来接收(管道的出口,只出不进)
	unbuffer := make(chan int)       // 无缓冲可收发
	buffer := make(chan int, 2)      // 有缓冲可收发
	println(senderOnly, receiverOnly, unbuffer, buffer)
}

        目前不知道前两种管道怎么用,因为不理解,如果只能发送,发送完了,也不能拿,哪种场景会用啊

下面介绍一些channel的使用场景:

1.2.1 等待goroutine完成

package main

func main() {
    println("start main")
    ch := make(chan bool)
    go func() {
        println("come into goroutine")
        ch <- true
    }()

    println("do something else")
    <-ch
    close(ch)

    println("end main")
}

运行结果:

(base) desktop-6rkqqec:study songbw$ go run test.go 
start main
do something else
come into goroutine
end main

1.2.2 多个goroutine协同

        三个功能不相关的goroutine最后结果要汇总累加到result

package main

func main() {
    println("start main")
    ch := make(chan int)

    var result int
    go func() {
        println("come into goroutine1")
        var r int
        for i := 1; i <= 10; i++ {
            r += i
        }
        ch <- r
    }()

    go func() {
        println("come into goroutine2")
        var r int = 1
        for i := 1; i <= 10; i++ {
            r *= i
        }
        ch <- r
    }()

    go func() {
        println("come into goroutine3")
        ch <- 11
    }()

    for i := 0; i < 3; i++ {
        result += <-ch
    }
    close(ch)
    println("result is:", result)
    println("end main")
}

运行结果:

(base) desktop-6rkqqec:study songbw$ go run test.go 
start main
come into goroutine3
come into goroutine1
come into goroutine2
result is: 3628866
end main

1.2.3 channel 和 range

package main

import (
    "fmt"
    "time"
)

func main() {
    
    c := make(chan int, 10)
    
    go fibonacci(cap(c), c)
    // range 函数遍历每个从通道接收到的数据,因为 c 在发送完 10 个
    // 数据之后就关闭了通道,所以这里我们 range 函数在接收到 10 个数据
    // 之后就结束了。如果上面的 c 通道不关闭,那么 range 函数就不
    // 会结束,从而在接收第 11 个数据的时候就阻塞了。
    
    for v := range c {
        fmt.Println("out:", time.Now())
        fmt.Println(v)
    }
}

func fibonacci(n int, c chan int) {
    x, y := 0, 1
    
    for i :=0; i < n; i++ {
        c <- x
        fmt.Println("in:",time.Now())
        time.Sleep(100)
        x, y = y, x+y
    }
    
    close(c)
}

执行结果:

(base) desktop-6rkqqec:study songbw$ go run channelWithRange.go 
in: 2020-03-26 10:29:49.164717 +0800 CST m=+0.000076459
out: 2020-03-26 10:29:49.164727 +0800 CST m=+0.000087213
in: 2020-03-26 10:29:49.164949 +0800 CST m=+0.000308986
0
in: 2020-03-26 10:29:49.164957 +0800 CST m=+0.000316599
in: 2020-03-26 10:29:49.164962 +0800 CST m=+0.000322119
out: 2020-03-26 10:29:49.164957 +0800 CST m=+0.000316850
1
out: 2020-03-26 10:29:49.164969 +0800 CST m=+0.000329174
1
out: 2020-03-26 10:29:49.164973 +0800 CST m=+0.000332509
2
out: 2020-03-26 10:29:49.164976 +0800 CST m=+0.000335379
3
in: 2020-03-26 10:29:49.164965 +0800 CST m=+0.000324719
in: 2020-03-26 10:29:49.165008 +0800 CST m=+0.000367753
in: 2020-03-26 10:29:49.165011 +0800 CST m=+0.000371055
in: 2020-03-26 10:29:49.165013 +0800 CST m=+0.000373340
in: 2020-03-26 10:29:49.165016 +0800 CST m=+0.000375470
in: 2020-03-26 10:29:49.165018 +0800 CST m=+0.000377607
out: 2020-03-26 10:29:49.165013 +0800 CST m=+0.000373113
5
out: 2020-03-26 10:29:49.16505 +0800 CST m=+0.000409584
8
out: 2020-03-26 10:29:49.165053 +0800 CST m=+0.000413031
13
out: 2020-03-26 10:29:49.165056 +0800 CST m=+0.000415929
21
out: 2020-03-26 10:29:49.165059 +0800 CST m=+0.000418730
34

      关闭通道并不会丢失里面的数据,只是让读取通道数据的时候不会读完之后一直阻塞等待新数据写入

1.2.4 channel 与 select

在介绍这个例子之前,先介绍一点基础的知识

a、select 相关

类似于switch关键字,只不过它的操作都是和channel有关的,知道两点即可:

1)当有多个case同时满足条件,系统会随机选择一个 case下的语句执行

2)但是所有的 case 都会执行

b、time.After(duration)接口

1)、此接口不会阻塞,它会立即返回一个只读的时间类型的<-channel

2)、经过 duration时间后,会往管道中塞一个当前时间

package main

import "time"

func main() {
    println("start main")
    cond1 := make(chan int)
    cond2 := make(chan uint64)

    go func() {
        for i := 0; ; i++ {
            cond1 <- i
        }
    }()

    go func() {
        var i uint64
        for ; ; i++ {
            cond2 <- i
        }
    }()

    endCond := false
    for endCond != true {
        select {
        case a := <-cond1:
            if a > 99 {
                println("end with cond1")
                endCond = true
            }
        case b := <-cond2:
            if b == 100 {
                println("end with cond2")
                endCond = true
            }
        case <-time.After(time.Microsecond):
            println("end with timeout")
            endCond = true
        }
    }

    println("end main")
}

执行结果,可能是

(base) localhost:study songbw$ go run test.go 
start main
end with cond1
end main

也可能是

(base) localhost:study songbw$ go run test.go 
start main
end with cond2
end main

还可能是 

(base) localhost:study songbw$ go run test.go 
start main
end with timeout
end main

        我是运行了3次,这三种结果都出现了,从侧面也反映了,每次 select,case后面的语句都会执行,而刚好100次循环大致需要1ms的时间,所以三个 case 几乎同时满足条件,所以系统就随机执行,某一个case 后面的语句

      文章  https://www.cnblogs.com/tobycnblogs/p/9935465.html 感觉是 channel的高级篇章,等用到了再看吧

 

参考文章

 https://www.jianshu.com/p/066455276de7

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值