当从缓冲通道读取时,所有的goroutine都处于休眠状态。

huangapple go评论114阅读模式
英文:

All goroutines are asleep when reading from buffered channel

问题

func writeToChan(wg *sync.WaitGroup, ch chan int, stop int) {
    defer wg.Done()
    for i := 0; i < stop; i++ {
        ch <- i
    }
}

func readToChan(wg *sync.WaitGroup, ch chan int) {
    defer wg.Done()
    for n := range ch {
        fmt.Println(n)
    }
}

func main() {
    ch := make(chan int, 3)
    wg := new(sync.WaitGroup)    

    wg.Add(2)
    go writeToChan(wg, ch, 5)
    go readToChan(wg, ch)

    wg.Wait()
}
0
1
2
3
4
fatal error: all goroutines are asleep - deadlock!

我假设readToChan函数总是连续读取,而writeToChan函数在向通道写入数据后会等待通道被读取。
我不知道为什么输出显示死锁,尽管我已经在WaitGroup中添加了两个wait操作。

英文:

func writeToChan(wg *sync.WaitGroup, ch chan int, stop int) {
    defer wg.Done()
    for i := 0; i &lt; stop; i++ {
        ch &lt;- i
    }
}

func readToChan(wg *sync.WaitGroup, ch chan int) {
    defer wg.Done()
    for n := range ch {
        fmt.Println(n)
    }
}

func main() {
    ch := make(chan int, 3)
    wg := new(sync.WaitGroup)    


    wg.Add(2)
    go writeToChan(wg, ch, 5)
    go readToChan(wg, ch)

    wg.Wait()
}
0
1
2
3
4
fatal error: all goroutines are asleep - deadlock!

I assume that the readToChan always reads continuously, and the writeToChan write to the channel and waits while the channel is read.
I don't know why the output showed deadlock while I added two 'wait' to the WaitGroup.

答案1

得分: 2

你需要在发送方关闭通道。
使用以下代码:

for n := range ch {
    fmt.Println(n)
}

只有当ch被关闭时,循环才会停止。

正确的示例代码如下:

package main

import (
	"fmt"
	"sync"
)

func writeToChan(wg *sync.WaitGroup, ch chan int, stop int) {
    defer wg.Done()
    for i := 0; i < stop; i++ {
        ch <- i
    }
    close(ch)
}

func readToChan(wg *sync.WaitGroup, ch chan int) {
    defer wg.Done()
    for n := range ch {
        fmt.Println(n)
    }
}

func main() {
    ch := make(chan int, 3)
    wg := new(sync.WaitGroup)    


    wg.Add(2)
    go writeToChan(wg, ch, 5)
    go readToChan(wg, ch)

    wg.Wait()
}
英文:

You need to close channel at the sender side.
By using

for n := range ch {
    fmt.Println(n)
}

The loop will only stop when ch is closed

correct example:

package main

import (
	&quot;fmt&quot;
	&quot;sync&quot;
)

func writeToChan(wg *sync.WaitGroup, ch chan int, stop int) {
    defer wg.Done()
    for i := 0; i &lt; stop; i++ {
        ch &lt;- i
    }
    close(ch)
}

func readToChan(wg *sync.WaitGroup, ch chan int) {
    defer wg.Done()
    for n := range ch {
        fmt.Println(n)
    }
}

func main() {
    ch := make(chan int, 3)
    wg := new(sync.WaitGroup)    


    wg.Add(2)
    go writeToChan(wg, ch, 5)
    go readToChan(wg, ch)

    wg.Wait()
}

答案2

得分: 0

如果没有在缓冲通道上调用close函数,读取器就不知道何时停止读取。请查看以下示例,其中使用for和select调用来处理多个通道。

https://go.dev/play/p/Lx5g9o4RsqW

package main
import (
	"fmt"
	"sync"
	"time"
)

func writeToChan(wg *sync.WaitGroup, ch chan int, stop int, quit chan<- bool) {
	defer func() {
		wg.Done()
		close(ch)
		fmt.Println("write wg done")
	}()

	for i := 0; i < stop; i++ {
		ch <- i
		fmt.Println("write:", i)
	}

	fmt.Println("write done")
	fmt.Println("sleeping for 5 sec")
	time.Sleep(5 * time.Second)
	quit <- true
	close(quit)
}

func readToChan(wg *sync.WaitGroup, ch chan int, quit chan bool) {
	defer func() {
		wg.Done()
		fmt.Println("read wg done")
	}()

	for {
		select {
		case n := <-ch:
			fmt.Println("read:", n)
		case val := <-quit:
			fmt.Println("received quit:", val)
			return
		}
	}
}

func main() {
	ch := make(chan int, 5)
	ch2 := make(chan bool)
	wg := new(sync.WaitGroup)
	wg.Add(2)

	go writeToChan(wg, ch, 3, ch2)
	go readToChan(wg, ch, ch2)

	wg.Wait()
}

输出:

write: 0
write: 1
write: 2
write done
sleeping for 5 sec
read: 0
read: 1
read: 2
write wg done
received quit: true
read wg done

程序已退出。
英文:

If close is not called on buffered channel, reader doesn't know when to stop reading.
Check this example with for and select calls(to handle multi channels).

https://go.dev/play/p/Lx5g9o4RsqW

    package main 
    import (
    &quot;fmt&quot;
    &quot;sync&quot;
    &quot;time&quot;) 
func writeToChan(wg *sync.WaitGroup, ch chan int, stop int, quit chan&lt;- bool) {
    defer func() {
    	wg.Done()
    	close(ch)
    	fmt.Println(&quot;write wg done&quot;)
    
    }()
    for i := 0; i &lt; stop; i++ {
    	ch &lt;- i
    	fmt.Println(&quot;write:&quot;, i)
    }
    fmt.Println(&quot;write done&quot;)
    fmt.Println(&quot;sleeping for 5 sec&quot;)
    time.Sleep(5 * time.Second)
    quit &lt;- true
    close(quit)
}
func readToChan(wg *sync.WaitGroup, ch chan int, quit chan bool) {
    defer func() {
    	wg.Done()
    	fmt.Println(&quot;read wg done&quot;)
    }()
    //using rang over
    //for n := range ch {
    //	fmt.Println(n)
    //}
    //using Select if you have multiple channels.
    for {
    	//fmt.Println(&quot;waiting for multiple channels&quot;)
    	select {
    	case n := &lt;-ch:
    		fmt.Println(&quot;read:&quot;, n)
    		// if ok == false {
    		// 	fmt.Println(&quot;read done&quot;)
    		// 	//return
    		// }
    	case val := &lt;-quit:
    		fmt.Println(&quot;received quit :&quot;, val)
    		return
    		// default:
    		// 	fmt.Println(&quot;default&quot;)
    	}
    }
}
func main() {
    ch := make(chan int, 5)
    ch2 := make(chan bool)
    wg := new(sync.WaitGroup)
    wg.Add(2)
    go writeToChan(wg, ch, 3, ch2)
    go readToChan(wg, ch, ch2)
    wg.Wait()
}

Output:

write: 0
write: 1
write: 2
write done
sleeping for 5 sec
read: 0
read: 1
read: 2
write wg done
received quit : true
read wg done

Program exited.

huangapple
  • 本文由 发表于 2022年8月16日 13:34:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/73369181.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定