总是与渠道陷入僵局
Always getting deadlock with channels
我正在学习使用 Go 通道,但总是遇到死锁。这段代码可能有什么问题?当数组大小不相等时,打印机随机停止工作;我想以某种方式通知打印机接收器停止工作会有所帮助。任何想法如何解决它?我的代码粘贴在下面。
package main
import (
"fmt"
"sync"
)
var wg = sync.WaitGroup{}
var wgs = sync.WaitGroup{}
var sg = make(chan int, 50)
var gp1 = make(chan int, 50)
var gp2 = make(chan int, 50)
func main(){
wgs.Add(2)
go Sender(0)
go Sender(11)
wg.Add(3)
go Receiver()
go Printer()
go Printer2()
wg.Wait()
}
func Sender(start int){
defer wgs.Done()
for i := start; i < 20; i++ {
sg <- i
}
}
func Receiver(){
defer wg.Done()
for i := 0; i < 20; i++{
nr := <- sg
if nr % 2 == 0{
gp1 <- nr
} else{
gp2 <- nr
}
}
}
func Printer(){
defer wg.Done()
var m [10]int
for i := 0; i < 10; i++ {
m[i] = <- gp1
}
wgs.Wait()
fmt.Println(m)
}
func Printer2(){
defer wg.Done()
var m [10]int
for i := 0; i < 10; i++ {
m[i] = <- gp2
}
wgs.Wait()
fmt.Println(m)
}
// Better to use this one
// func Receiver(senderChannel <-chan int, printerChannel1 chan<- int, printerChannel2 chan<- int, wg *sync.WaitGroup) {
发件人生成(我认为是 28 条消息)。其中前 20 个中大约有一半转到 gp1 和 gp2 之一。然后打印机和打印机 2 卸载消息
麻烦的是,Receiver 拆分消息的方式取决于收到的数字是奇数还是偶数。但是你不能控制这个。如果其中一台打印机的队列中的项目少于 10 个,它将挂起
这是一个潜在问题
你的核心问题是这里的一切都是"dead reckoning":他们希望看到固定数量的消息,但这不一定与现实相符。您应该设置通道,以便在生成所有数据后关闭它们。
这可能意味着设置一个中间函数来管理发送:
func Sender(from, to int, c chan<- int) {
for i := from; i < to; i++ {
c <- i
}
}
func SendEverything(c chan<- int) {
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
Sender(0, 20, c)
}()
go func() {
defer wg.Done()
Sender(11, 20, c)
}()
wg.Wait()
close(c)
}
使调度程序功能适用于频道中的所有内容:
func Receive(c <-chan int, odds, evens chan<- int) {
for n := range c {
if n%2 == 0 {
evens <- n
} else {
odds <- n
}
}
close(odds)
close(evens)
}
然后可以共享一个打印函数:
func Printer(prefix string, c <-chan int) {
for n := range c {
fmt.Printf("%s: %d\n", prefix, n)
}
}
最后,你有一个将它们拼接在一起的主要功能:
func main() {
var wg sync.WaitGroup
inputs := make(chan int)
odds := make(chan int)
evens := make(chan int)
wg.Add(4)
go func() {
defer wg.Done()
SendEverything(inputs)
}()
go func() {
defer wg.Done()
Receive(inputs, odds, evens)
}()
go func() {
defer wg.Done()
Printer("odd number", odds)
}()
go func() {
defer wg.Done()
Printer("even number", evens)
}()
wg.Wait()
}
完整示例位于 https://play.golang.org/p/qTUqlt-uaWH。
请注意,我已完全避免使用任何全局变量,并且任何事物都有一个希望不言自明的非常短的名称(i
和 n
是简单的整数,c
是一个频道)或者是完整的单词(odds
、evens
)。我倾向于将 sync.WaitGroup
对象保留在创建它们的地方。由于一切都作为参数传递,我不需要同一个函数的两个副本来作用于不同的全局变量,如果我选择为此编写测试代码,我可以创建我自己的本地频道。
我正在学习使用 Go 通道,但总是遇到死锁。这段代码可能有什么问题?当数组大小不相等时,打印机随机停止工作;我想以某种方式通知打印机接收器停止工作会有所帮助。任何想法如何解决它?我的代码粘贴在下面。
package main
import (
"fmt"
"sync"
)
var wg = sync.WaitGroup{}
var wgs = sync.WaitGroup{}
var sg = make(chan int, 50)
var gp1 = make(chan int, 50)
var gp2 = make(chan int, 50)
func main(){
wgs.Add(2)
go Sender(0)
go Sender(11)
wg.Add(3)
go Receiver()
go Printer()
go Printer2()
wg.Wait()
}
func Sender(start int){
defer wgs.Done()
for i := start; i < 20; i++ {
sg <- i
}
}
func Receiver(){
defer wg.Done()
for i := 0; i < 20; i++{
nr := <- sg
if nr % 2 == 0{
gp1 <- nr
} else{
gp2 <- nr
}
}
}
func Printer(){
defer wg.Done()
var m [10]int
for i := 0; i < 10; i++ {
m[i] = <- gp1
}
wgs.Wait()
fmt.Println(m)
}
func Printer2(){
defer wg.Done()
var m [10]int
for i := 0; i < 10; i++ {
m[i] = <- gp2
}
wgs.Wait()
fmt.Println(m)
}
// Better to use this one
// func Receiver(senderChannel <-chan int, printerChannel1 chan<- int, printerChannel2 chan<- int, wg *sync.WaitGroup) {
发件人生成(我认为是 28 条消息)。其中前 20 个中大约有一半转到 gp1 和 gp2 之一。然后打印机和打印机 2 卸载消息
麻烦的是,Receiver 拆分消息的方式取决于收到的数字是奇数还是偶数。但是你不能控制这个。如果其中一台打印机的队列中的项目少于 10 个,它将挂起
这是一个潜在问题
你的核心问题是这里的一切都是"dead reckoning":他们希望看到固定数量的消息,但这不一定与现实相符。您应该设置通道,以便在生成所有数据后关闭它们。
这可能意味着设置一个中间函数来管理发送:
func Sender(from, to int, c chan<- int) {
for i := from; i < to; i++ {
c <- i
}
}
func SendEverything(c chan<- int) {
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
Sender(0, 20, c)
}()
go func() {
defer wg.Done()
Sender(11, 20, c)
}()
wg.Wait()
close(c)
}
使调度程序功能适用于频道中的所有内容:
func Receive(c <-chan int, odds, evens chan<- int) {
for n := range c {
if n%2 == 0 {
evens <- n
} else {
odds <- n
}
}
close(odds)
close(evens)
}
然后可以共享一个打印函数:
func Printer(prefix string, c <-chan int) {
for n := range c {
fmt.Printf("%s: %d\n", prefix, n)
}
}
最后,你有一个将它们拼接在一起的主要功能:
func main() {
var wg sync.WaitGroup
inputs := make(chan int)
odds := make(chan int)
evens := make(chan int)
wg.Add(4)
go func() {
defer wg.Done()
SendEverything(inputs)
}()
go func() {
defer wg.Done()
Receive(inputs, odds, evens)
}()
go func() {
defer wg.Done()
Printer("odd number", odds)
}()
go func() {
defer wg.Done()
Printer("even number", evens)
}()
wg.Wait()
}
完整示例位于 https://play.golang.org/p/qTUqlt-uaWH。
请注意,我已完全避免使用任何全局变量,并且任何事物都有一个希望不言自明的非常短的名称(i
和 n
是简单的整数,c
是一个频道)或者是完整的单词(odds
、evens
)。我倾向于将 sync.WaitGroup
对象保留在创建它们的地方。由于一切都作为参数传递,我不需要同一个函数的两个副本来作用于不同的全局变量,如果我选择为此编写测试代码,我可以创建我自己的本地频道。