设为首页 加入收藏

TOP

Go基础系列:channel入门(二)
2018-11-20 00:14:18 】 浏览:376
Tags:基础 系列 channel 入门
直接receive数据,将会出现死锁:

package main 

import (
    "fmt"
)

func main (){
    goo(32)
}

func goo(s int) {
    counter := make(chan int)
    counter <- s
    fmt.Println(<-counter)
}

在上面的示例中,向unbuffered channel中send数据的操作counter <- s是在main goroutine中进行的,从此channel中recv的操作<-counter也是在main goroutine中进行的。send的时候会直接阻塞main goroutine,使得recv操作无法被执行,go将探测到此问题,并报错:

fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:

要修复此问题,只需将send操作放在另一个goroutine中执行即可:

package main

import (
    "fmt"
)

func main() {
    goo(32)
}

func goo(s int) {
    counter := make(chan int)
    go func() {
        counter <- s
    }()
    fmt.Println(<-counter)
}

或者,将counter设置为一个容量为1的buffered channel:

counter := make(chan int,1)

这样放完一个数据后send不会阻塞(被recv之前放第二个数据才会阻塞),可以执行到recv操作。

unbuffered channel同步通信示例

下面通过sync.WaitGroup类型来等待程序的结束,分析多个goroutine之间通信时状态的转换。因为创建的channel是unbuffered类型的,所以send和recv都是阻塞的。

package main

import (
    "fmt"
    "sync"
)

// wg用于等待程序执行完成
var wg sync.WaitGroup

func main() {
    count := make(chan int)

    // 增加两个待等待的goroutines
    wg.Add(2)
    fmt.Println("Start Goroutines")

    // 激活一个goroutine,label:"Goroutine-1"
    go printCounts("Goroutine-1", count)
    // 激活另一个goroutine,label:"Goroutine-2"
    go printCounts("Goroutine-2", count)

    fmt.Println("Communication of channel begins")
    // 向channel中发送初始数据
    count <- 1

    // 等待goroutines都执行完成
    fmt.Println("Waiting To Finish")
    wg.Wait()
    fmt.Println("\nTerminating the Program")
}
func printCounts(label string, count chan int) {
    // goroutine执行完成时,wg的计数器减1
    defer wg.Done()
    for {
        // 从channel中接收数据
        // 如果无数据可recv,则goroutine阻塞在此
        val, ok := <-count
        if !ok {
            fmt.Println("Channel was closed:",label)
            return
        }
        fmt.Printf("Count: %d received from %s \n", val, label)
        if val == 10 {
            fmt.Printf("Channel Closed from %s \n", label)
            // Close the channel
            close(count)
            return
        }
        // 输出接收到的数据后,加1,并重新将其send到channel中
        val++
        count <- val
    }
}

上面的程序中,激活了两个goroutine,激活这两个goroutine后,向channel中发送一个初始数据值1,然后main goroutine将因为wg.Wait()等待2个goroutine都执行完成而被阻塞。

再看这两个goroutine,这两个goroutine执行完全一样的函数代码,它们都接收count这个channel的数据,但可能是goroutine1先接收到channel中的初始值1,也可能是goroutine2先接收到初始值1。接收到数据后输出值,并在输出后对数据加1,然后将加1后的数据再次send到channel,每次send都会将自己这个goroutine阻塞(因为unbuffered channel),此时另一个goroutine因为等待recv而执行。当加1后发送给channel的数据为10之后,某goroutine将关闭count channel,该goroutine将退出,wg的计数器减1,另一个goroutine因等待recv而阻塞的状态将因为channel的关闭而失败,ok状态码将让该goroutine退出,于是wg的计数器减为0,main goroutine因为wg.Wait()而继续执行后面的代码。

使用for range迭代channel

前面都是在for无限循环中读取channel中的数据,但也可以使用range来迭代channel,它会返回每次迭代过程中所读取的数据,直到channel被关闭。

例如,将上面示例中的printCounts()改为for-range的循环形式。

func printCounts(label string, count chan int) {
    defer wg.Done()
    for val := range count {
        fmt.Printf("Count: %d received from %s \n", val, label)
        if val == 10 {
            fmt.Printf("Channel Closed from %s \n", label)
            close(count)
            return
        }
        val++
        count <- val
    }
}

多个"管道":输出作为输入

channel是goroutine与goroutine之间通信的基础,一边产生数据放进channel,另一边从channel读取放进来的数据。可以借此实现多个goroutine之间的数据交换,例如goroutine_1->goroutine_2->goroutine_3,就像bash的管道一样,上一个命令的输出可以不断传递给下一个命令的输入,只不过golang借助channel可以在多

首页 上一页 1 2 3 4 下一页 尾页 2/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇casbin-权限管理 下一篇go 交叉编译,部署

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目