Golang 之 goroutine与channel

goroutine        

使用goroutine的方法很简单,直接在语句前面加go关键字即可,如果是多核处理器的电脑,使用gorountine,就会在另外一个CPU上执行goroutine,子协程不一定会和主协程在一个CPU上执行。

        这里有两个注意的地方,使用go关键字的进程称之为子协程,而没有使用go关键字的进程称之为主协程,在多CPU的机器上,如果有多个协程,那么这些协程的执行顺序以及执行完成的顺序都是不确定的,但有一点,如果主协程结束,那么整个进程就结束了,不论子协程是否结束,整个进程都结束了,也就看不到子协程的运行结果了。

package main
import "fmt"


func main() {
    go fmt.Println("hello")  //子协程
    fmt.Println("world")   //主协程
}

上面的代码执行之后,会有三种结果:

1、hello world      2、world       3、world hello

  上面这三个结果都是正确的。

  对于第一种,子协程在一个CPU上面执行输出hello之后,再轮到主协程中执行输出world。

  对于第二种,子协程在一个CPU上还没来得及执行,或者说没有执行完成,但是主协程已经执行完成了,此时整个进程就运行完毕了,所以就看不到子协程的运行结果了。

  对于第三种,主协程中语句还没执行完成的时候,子协程已经运行完毕,几乎同时完成,所以会出现着这种情况。

Channel

channel的零值是nil

//创建channel
chi := make(chan int)
chs := make(chan string)
chif := make(chan interface{})
//因为可以认为所有的类型都实现了空接口
//所以这个chif的chan接收任何类型的chan

// 发送和接收channel
chi := make(chan int)
chi <- 3 //发送
i := <-chi   //接收,保存值
<-chi        //接收,不保存值
close(chi) //关闭channel
//对一个已经关闭的channel发送数据,都会导致panic异常。
//对于一个已经关闭的channel,可以继续接收这个已关闭的channel中的数据,如果没有数据的话,接收到的就是chan类型的零值。

// 声明单向channle
chi := make(<-chan int) //只能被读数据的channel
chi := make(chan <- int) //只接收数据的channel

// 有缓冲的channel
ch := make(chan int)       //无缓冲
chi := make(chan int,10)   //有缓冲channel,缓冲空间可存10个值

无缓冲的channel(同步channel)

有缓冲的channel(异步channel)

一个基于无缓冲的channel:

  1. 发送操作将导致发送者的gorountine阻塞,直到有另外一个goroutine在相同的channel上执行接收操作之后,发送者的阻塞才会解开。
  2. 同理,如果接收者的接收操作先发生,那么接收者因为从channel中接收不到数据,所有接收者goroutine也将阻塞,直到另一个goroutine在相同的channel上执行发送操作,接受者接收到了数据的阻塞才会解开。
  3. 向一个nil值(未用make分配空间)的channel发送或读取数据,会导致永远阻塞。

基于无缓冲的channel的发送和接收操作将导致两个goroutine做一次同步操作,所以也叫作同步channel。

使用range遍历channel

package main
import (
    "fmt"
)
func main() {
    num := make(chan int)
    go func() {
        num <- 30
        num <- 40
        close(num) // 注意
    }()
    for x := range num {
        fmt.Println(x)
    }
    //输出
    //30
    //40
}

// 使用range遍历channel时,从channel中接收数据,如果从channel中获取到了数据,那么返回的结果就是获取到的值;如果channel已经关闭,那么对于无缓冲的channel而言,就没有数据可获取了
// 需要注意的是,如果使用for 遍历channel的时候,channel没有close,那么会造成死锁。

因为关闭channel操作 一般是 发送方通知接收方,发送方不再向channel中发送新的数据了,所以通常只有在发送者所在的goroutine才会调用close函数,在接收channel数据的goroutine中关闭channel显然是不合理的,很有可能会引起错误,因为发送数据的goroutine可能不知道channel已经关闭了,所以他一旦向channle中发送数据,就会引发错误。

总结

  • 对写的一方来说,一定记着及时关闭channel,避免出现协程泄露。虽然它占得资源少,省点电不香么。
  • 对读的一方来说,除非十分确定数据的个数,最好是用for来读数据,省的在“管儿”里有“野数据”造成内存泄露。同时根据第二个返回值的真假来控制for循环,避免出现“无效工作量”
  • 对一个已经关闭的channel发送数据,都会导致panic异常。
  • 对于一个已经关闭的channel,可以继续接收这个已关闭的channel中的数据,如果没有数据的话,接收到的就是chan类型的零值。
  • 向一个nil值(未用make分配空间)的channel发送或读取数据,会导致永远阻塞。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值