go学习笔记-goroutine的好兄弟channel

上一篇介绍了atomic包以及互斥锁 mutex来解决并发竞争状态的问题。这一篇主要来介绍go中与goroutine常常搭档的好兄弟channel缓存

channel不只能够能够来用消除竞争状态,还能够用于不一样的 goroutine中进行通讯,发送与接受数据。chaanel的定义有两种,分为 有缓存无缓冲
建立channel
chan1 := make(chan int) // 建立一个无缓冲的 整形 channel
chan2 := make(chan int,2)// 建立一个有缓冲的 整形 channel

上面的代码片断,咱们分别建立了一个无缓冲的channel与一个有缓冲的channelchannel的建立是使用make(chan type,[lenght])来建立,若是指定了第二个参数length表示建立了一个长度为length的有缓存channel,反之咱们称之为无缓冲。并发

channel的值传递
var number int
func main()  {
    chan1 := make(chan int) //建立一个无缓冲的 整形channel
    go numberAdd(chan1)
    fmt.Printf("改变以后的number:%d\r\n",<-chan1)
    //改变以后的number:1
}
func numberAdd(c chan int)  {
    number++;
    c<-number; //往chan写值
}

这里咱们建立了一个整形的channelgoroutine中往chan中写值,在main函数中取值。函数

无缓冲与有缓存通道的区别
  • 无缓冲通道:是指在接收前没有能力保存任何值的通道。无缓冲的通道要求发送和接收的goroutine 同时准备好才能完成发送和接收操做。若是两个 goroutine没有同时准备好,通道会致使先执行发送或接收操做的 goroutine阻塞等待。
  • 有缓冲通道:在在被接收前能够接受一个或多个值。有缓冲通道不要求发送与接受的groutine同时准备好。只有在通道中没有空间容纳新值的时候,发送动做才会发送阻塞;只有在通道中没有值要接收时,接收动做才会阻塞。
  • 区别:无缓冲通道能够保证接收跟发送数据是在同一时间,而有缓存通道则不能保证这一点。

下面来看两个例子atom

无缓存通道
var wait sync.WaitGroup
const  needProcessNumber = 3 //须要三次加工
func main()  {
    wait.Add(1)
    sausage := make(chan int) // 腊肠
    go processing(sausage) //开始加工程序
    sausage<-1 //开始第一次加工
    wait.Wait()
}

func processing(sausage chan int)  {
    defer wait.Done()
    for  {
        nowNumber := <-sausage
        fmt.Printf("第%d次加工开始\r\n",nowNumber)
        for i:=1; i<=10; i++ {
            fmt.Printf("%d \r\n",i*10)
        }
        fmt.Printf("第%d次加工结束\r\n",nowNumber)
        if nowNumber==needProcessNumber{
            fmt.Printf("新鲜的腊肠出炉了\r\n")
            close(sausage)
            return
        }else {
            go processing(sausage) //等待下一次加工开始
        }
        nowNumber++
        sausage <- nowNumber
        //这里会加锁直到流程交接结束
    }
}

这个例子建立了一个Int无缓冲通道来表示腊肠,作一个腊肠须要三次加工,main函数中建立了一个wait来等待加工完成。准备一个加工的goroutine processing等待第一个杯子准备就绪的信号,当接收到第一个信号时,开始加工,而后等待当前加工完成,若是当前goroutine不是第三次加工的goroutine,那么准备下一个加工程序开始,进入下一个goroutine,直到第三次加工完成。spa

有缓存通道
var wait sync.WaitGroup
const (
    maxTask = 10 //最大处理工做数
    workerNumber = 3 //当前工人数
)
func main()  {
    wait.Add(workerNumber) //等到全部的work都结束
    tasks := make(chan int,maxTask)
    for workerOnline:=1;workerOnline<=workerNumber;workerOnline++ {
        go worker(tasks,workerOnline)
    }
    //增长十个须要处理的工做
    for i:=1;i<=maxTask ; i++ {
        tasks<-i
    }
    close(tasks)//全部工做完成
    wait.Wait()
}
//员工开始工做
func worker(task chan int,workNumber int)  {
    defer  wait.Done()
    for{
        taskNumber,ok := <-task
        if !ok {
            fmt.Printf("工人:%d 没有工做能够作了\r\n",workNumber)
            return
        }
        fmt.Printf("工人:%d 开始工做,当前任务编号:%d\r\n",workNumber,taskNumber)
        workTime := rand.Int63n(100)
        time.Sleep(time.Duration(workTime)*time.Millisecond)
        fmt.Printf("工人:%d 工做完成,当前任务编号:%d\r\n",workNumber,taskNumber)
    }
}

这里咱们声明了一个容量为10的有缓冲通道task来表示总共有十个任务须要3个员工来处理。每一个员工是一个goroutine来单独完成工做。员工首先准备就绪,而后等待任务的下发。当监听到有任务进入时,开始完成工做,直到监听到task通道已经关闭。须要注意的是咱们在新增完10个任务时就已经关闭了channel,这个时候goroutine仍然能够从channel取值,直到取到的返回数值是零值,若是你这个时候获取了channel的标志位,那么会返回一个false,因此咱们判断channel是否关闭应该用这个标志位来判断。code

期待一块儿交流

短腿子猿

相关文章
相关标签/搜索