快速吃透 Golang Channels
2024-6-8 23:57:11 Author: cloudsjhan.github.io(查看原文) 阅读量:21 收藏

发表于 | 分类于 | 阅读次数: |

| 字数统计: 1,054 | 阅读时长 ≈ 4

Golang 可以通过启动 goroutines 来并发执行任务。它们可以通过一种名为 “通道”的通信媒介相互通信。话不多说,下面我列举了几种不同情况下 channel 的使用以及其适用条件,最后总结出一张 channel table,能够帮助你快速吃透 channel 的所有要点。Let’t go!

Nil Channels

如果你像创建普通变量一样创建一个通道,通道将被初始化为零值。这里要提到的另一个重要细节是,通道的发送方和接收方都必须做好通信准备,否则我们将收到一个死锁错误。这可以通过缓冲通道来避免,我们将在后面讨论。

1
2
3
4
5
6
7
8
9
10
11
// NOTE: Deadlock
func nilChannel() {
var ch chan int

// creating a goroutine and sending value on the channel
go func() {
ch <- 1
}()

fmt.Println(<-ch)
}

如果你在 main 中调用这个函数,你会得到以下错误和一些其他信息。

1
fatal error: all goroutines are asleep - deadlock!

这是因为我们试图在往 nil channel上发送一个值,这是坚决不允许的操作。

Empty Channel

在以下代码中,我们将收到同样的死锁错误,因为我们正试图在 channel receiver 尚未准备好的通道上发送值。从下面的代码中可以看到,在接收器创建之前,值就已经在通道上发送了。

1
2
3
4
5
6
7
8
// NOTE: Deadlock
func emptyChannel() {
var ch = make(chan int)

ch <- 1

fmt.Println(<-ch)
}

UN Buffered Channel

错误的代码已经够多了。这次让我给出一个有效的代码。在下面的代码中,我们编写了与上一个示例相同的代码,但不是创建一个 nil 通道,而是使用 make 函数创建了一个用默认值初始化的通道。 不过,如果在这种情况下没有接收器,就会错过结果,但不会产生死锁错误。

1
2
3
4
5
6
7
8
9
10
11
// NOTE: send single value through goroutine
func simpleChannel() {
var ch = make(chan int)

go func() {
ch <- 1
}()

// NOTE: if you comment this line. You will not be able to receive the result but code will not crash
fmt.Println(<-ch)
}

Channel 方向

默认情况下通道是双向的。下面的代码显示了一个只能用于发送值的通道。如果我们试图从中获取值,就会出错。

1
2
3
4
5
6
7
8
9
10
11
func uniDirectionalChannel() {
// Bidirectional [outside goroutine]
var ch = make(chan int)

go func(ch chan<- int) {
// unidirectinal [within goroutine]
ch <- 1
}(ch)

fmt.Println(<-ch)
}

Buffered Channel

这些通道可以像数组一样容纳多个值,因此在非缓冲通道中,如果我们尝试在没有接收器的情况下向其写入数据,就会出错,但在缓冲通道中,我们可以向其写入数据,直到缓冲区满为止。当缓冲区已满时,如果我们尝试向其中写入新值,就会出错。
如果我们这里不注释该函数的最后一行,就会出现死锁错误,因为这里将从空通道读取数据。

1
2
3
4
5
6
7
8
9
10
11
// using buffered channel
func bufferdChannelWithoutLoop() {
var ch = make(chan int, 2)

ch <- 1
ch <- 2

fmt.Println(<-ch)
fmt.Println(<-ch)
// fmt.Println(<-ch) // NOTE: Deadlock, Reading from empty channel
}

从通道读取数值还有另一种方法,即使用循环。在前面的示例中,我们逐个读取数值,但我们也可以通过循环读取接收通道中的数值,因此每当通道中发送一个新数值时,循环就会迭代,执行完正文中的代码后,就会等待下一个数值。如果接收器试图读取,但通道上已没有其他值,则会出现同样的死锁错误。

开发人员的职责是在通道使用后将其关闭,因为如果接收器试图读取已经关闭的通道,就会出现上述死锁问题。

Channels Table

我们已经讨论了通道的所有情况,但怎么才能快速记住它们呢? 别紧张😎,有我在,下表可以作为快速指南,对照着表盘一下我们写的 channel 就能避免出现死锁,并显示它们在不同情况下的行为。


-------------The End-------------

cloud sjhan wechat

subscribe to my blog by scanning my public wechat account

0%


文章来源: https://cloudsjhan.github.io/2024/06/08/%E5%BF%AB%E9%80%9F%E5%90%83%E9%80%8F-Golang-Channels/
如有侵权请联系:admin#unsafe.sh