关于关键字 "go" 和没有 Goroutine 的比较
comparation about keyword "go" and without in Goroutine
以下代码记录了一个错误:
fatal error: all goroutines are asleep - deadlock!
package main
import "fmt"
func main() {
ch := make(chan int)
ch <- 1
fmt.Println(<-ch)
}
但是当我把代码改成这样的时候:
package main
import "fmt"
func assign (ch chan int) {
ch <- 1
}
func main() {
ch := make(chan int)
go assign (ch)
fmt.Println(<-ch)
}
打印出“1”。
然后我使用了缓冲通道:
package main
import "fmt"
func main() {
ch := make(chan int, 2)
ch <- 1
ch <- 2
fmt.Println(<-ch)
fmt.Println(<-ch)
}
“1”和“2”也可以打印出来
我对这种情况有点困惑。提前致谢!
为什么会出现死锁:
在第一个代码片段中,您只有一个主 goroutine,当您尝试写入通道时它被阻止了:
ch <- 1
因为没有人从通道中读取并且主 goroutine 正在等待它继续。
If the channel is unbuffered, the sender blocks until the receiver has received the value.
发送方是main
函数,接收方也是main
函数。
如何避免死锁:
为了解决这个问题,你有两个选择:
选项 1: 使 ch
通道像这样缓冲:
ch := make(chan int, 1) // buffer length is set to 1
Sends to a buffered channel block only when the buffer is full.
因此,您可以写入通道,直到缓冲区已满。然后有人必须从频道开始阅读。
选项 2: 从 goroutine 写入通道,就像您在第二个代码片段中所做的那样:
func assign(ch chan int) {
ch <- 1
}
func main() {
ch := make(chan int)
go assign(ch) // does not block the main goroutine
fmt.Println(<-ch) // waiting to read from the channel
}
在这种情况下,main
函数将执行到 fmt.Println(<-ch)
并在可以从通道读取后立即继续。
当您使用无缓冲通道时,goroutine 在写入期间被阻塞,直到有人进行读取。
在您的第一个片段中,有一个无缓冲通道和单个 goroutine(主 goroutine)。
因此,当您尝试编写时:
ch <- 1
还没有人从频道读取。主 goroutine 被阻塞,此行永远不会执行:
fmt.Println(<-ch)
这就是您出现死锁错误的原因。
在第二个示例中,您仍然使用无缓冲通道,这意味着写入操作会阻塞 goroutine。
但是通过使用 go
你是 运行 第二个 goroutine。
这意味着即使这个新的 goroutine 在写入过程中被阻塞(在你的 assign
函数中),主 goroutine 将继续工作并且 fmt.Println(<-ch)
将被执行并进行读取(这反过来又会解锁后台 goroutine assign
函数最终会到达终点)。
为了更好地了解通道和 goroutines,此代码段将给出相同的结果(与您的第二个代码段):
package main
import "fmt"
func print(ch chan int) {
fmt.Println(<-ch)
}
func main() {
ch := make(chan int)
go print(ch)
ch <- 1
}
当您使用缓冲通道(第三个片段)时,您可以执行 N
写操作而不会阻塞 goroutine(其中 N
是缓冲区的大小)。
这就是为什么在您的示例中您没有阻塞地进行了 2 次写入并且稍后能够读取它们的原因。但是如果你的缓冲区小于写操作的次数,并且没有人进行读取,你将陷入同样的阻塞问题(参见 1&2 片段的解释)。
以下代码记录了一个错误:
fatal error: all goroutines are asleep - deadlock!
package main
import "fmt"
func main() {
ch := make(chan int)
ch <- 1
fmt.Println(<-ch)
}
但是当我把代码改成这样的时候:
package main
import "fmt"
func assign (ch chan int) {
ch <- 1
}
func main() {
ch := make(chan int)
go assign (ch)
fmt.Println(<-ch)
}
打印出“1”。
然后我使用了缓冲通道:
package main
import "fmt"
func main() {
ch := make(chan int, 2)
ch <- 1
ch <- 2
fmt.Println(<-ch)
fmt.Println(<-ch)
}
“1”和“2”也可以打印出来
我对这种情况有点困惑。提前致谢!
为什么会出现死锁:
在第一个代码片段中,您只有一个主 goroutine,当您尝试写入通道时它被阻止了:
ch <- 1
因为没有人从通道中读取并且主 goroutine 正在等待它继续。
If the channel is unbuffered, the sender blocks until the receiver has received the value.
发送方是main
函数,接收方也是main
函数。
如何避免死锁:
为了解决这个问题,你有两个选择:
选项 1: 使 ch
通道像这样缓冲:
ch := make(chan int, 1) // buffer length is set to 1
Sends to a buffered channel block only when the buffer is full.
因此,您可以写入通道,直到缓冲区已满。然后有人必须从频道开始阅读。
选项 2: 从 goroutine 写入通道,就像您在第二个代码片段中所做的那样:
func assign(ch chan int) {
ch <- 1
}
func main() {
ch := make(chan int)
go assign(ch) // does not block the main goroutine
fmt.Println(<-ch) // waiting to read from the channel
}
在这种情况下,main
函数将执行到 fmt.Println(<-ch)
并在可以从通道读取后立即继续。
当您使用无缓冲通道时,goroutine 在写入期间被阻塞,直到有人进行读取。 在您的第一个片段中,有一个无缓冲通道和单个 goroutine(主 goroutine)。 因此,当您尝试编写时:
ch <- 1
还没有人从频道读取。主 goroutine 被阻塞,此行永远不会执行:
fmt.Println(<-ch)
这就是您出现死锁错误的原因。
在第二个示例中,您仍然使用无缓冲通道,这意味着写入操作会阻塞 goroutine。
但是通过使用 go
你是 运行 第二个 goroutine。
这意味着即使这个新的 goroutine 在写入过程中被阻塞(在你的 assign
函数中),主 goroutine 将继续工作并且 fmt.Println(<-ch)
将被执行并进行读取(这反过来又会解锁后台 goroutine assign
函数最终会到达终点)。
为了更好地了解通道和 goroutines,此代码段将给出相同的结果(与您的第二个代码段):
package main
import "fmt"
func print(ch chan int) {
fmt.Println(<-ch)
}
func main() {
ch := make(chan int)
go print(ch)
ch <- 1
}
当您使用缓冲通道(第三个片段)时,您可以执行 N
写操作而不会阻塞 goroutine(其中 N
是缓冲区的大小)。
这就是为什么在您的示例中您没有阻塞地进行了 2 次写入并且稍后能够读取它们的原因。但是如果你的缓冲区小于写操作的次数,并且没有人进行读取,你将陷入同样的阻塞问题(参见 1&2 片段的解释)。