所有的goroutine都处于休眠状态,发生了死锁。

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

all goroutines are asleep deadlock

问题

我正在尝试使用goroutines和channel进行实验。

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func boring(msg string) <-chan string {
	c := make(chan string)
	go func() {
		for i := 0; ; i++ {
			c <- fmt.Sprintf("%s %d", msg, i)
			time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
		}
	}()
	return c
}

func main() {
	c := fanInNew(boring("joe"), boring("anh"))
	for i := 0; i < 10; i++ {
		fmt.Println(<-c)
	}
	fmt.Println("You both are boring, I am leaving")
}

func fanInNew(input1, input2 <-chan string) <-chan string {
	c := make(chan string)
	for {
		select {
		case s := <-input1:
			c <- s
		case s := <-input2:
			c <- s
		}
	}
	return c
}

如果我运行这个程序,它会给我一个错误,所有的goroutines都处于睡眠状态,发生了死锁。

但是,如果我将select放在匿名goroutine内部,它就可以正常工作。下面是一个可工作的示例:

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func boring(msg string) <-chan string {
	c := make(chan string)
	go func() {
		for i := 0; ; i++ {
			c <- fmt.Sprintf("%s %d", msg, i)
			time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
		}
	}()
	return c
}

func main() {
	c := fanInNew(boring("joe"), boring("anh"))
	for i := 0; i < 10; i++ {
		fmt.Println(<-c)
	}
	fmt.Println("You both are boring, I am leaving")
}

func fanInNew(input1, input2 <-chan string) <-chan string {
	c := make(chan string)
	go func() {
		for {
			select {
			case s := <-input1:
				c <- s
			case s := <-input2:
				c <- s
			}
		}
	}()
	return c
}

你能帮我理解其中的原因吗?

英文:

I am trying to play around with goroutines and channel

    package main

import (
	&quot;fmt&quot;
	&quot;math/rand&quot;
	&quot;time&quot;
)

func boring(msg string) &lt;-chan string {
	c := make(chan string)
	go func() {
		for i := 0; ; i++ {
			c &lt;- fmt.Sprintf(&quot;%s %d&quot;, msg, i)
			time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
		}
	}()
	return c
}
func main() {
	c := fanInNew(boring(&quot;joe&quot;), boring(&quot;anh&quot;))
	for i := 0; i &lt; 10; i++ {
		fmt.Println(&lt;-c)
	}
	fmt.Println(&quot;You both are boring, I am leaving&quot;)
}

func fanInNew(input1, input2 &lt;-chan string) &lt;-chan string {
	c := make(chan string)
	for {
		select {
		case s := &lt;-input1:
			c &lt;- s
		case s := &lt;-input2:
			c &lt;- s
		}
	}
	return c
}

If i run this program it is giving me error, all goroutines are asleep, deadlock.

But If I put select inside anonymous goroutine, it works just fine. Working example:

    package main

import (
	&quot;fmt&quot;
	&quot;math/rand&quot;
	&quot;time&quot;
)

func boring(msg string) &lt;-chan string {
	c := make(chan string)
	go func() {
		for i := 0; ; i++ {
			c &lt;- fmt.Sprintf(&quot;%s %d&quot;, msg, i)
			time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
		}
	}()
	return c
}
func main() {
	c := fanInNew(boring(&quot;joe&quot;), boring(&quot;anh&quot;))
	for i := 0; i &lt; 10; i++ {
		fmt.Println(&lt;-c)
	}
	fmt.Println(&quot;You both are boring, I am leaving&quot;)
}

func fanInNew(input1, input2 &lt;-chan string) &lt;-chan string {
	c := make(chan string)
	go func() {
		for {
			select {
			case s := &lt;-input1:
				c &lt;- s
			case s := &lt;-input2:
				c &lt;- s
			}
		}
	}()
	return c
}

Can you please help me to understand reasoning behind it.

答案1

得分: 3

for语句会无限循环,因此&lt;-c通道永远不会被传递,通道会被填满,但main线程会因为等待c := fanInNew(a, b)而被阻塞。

fanInNew()函数永远不会返回,因为for循环永远不会结束(而且在select语句上会阻塞):

func fanInNew(input1, input2 <-chan string) <-chan string {
    c := make(chan string)
    for { // 无限循环并从输入1和输入2中读取数据
        select {
        case s := <-input1:
            c <- s
        case s := <-input2:
            c <- s            
        }
    }
    return c
}

然后在主线程中,这个函数永远不会返回c通道:

func main() {
    // 永远不会执行到下一行
    c := fanInNew(boring("joe"), boring("anh"))
}

因此,你可以将for循环本身放在goroutine中,就像你在第二个示例中所做的那样。此外,通常goroutine应该返回,要么是因为传递了一条消息(例如通过close()),要么是因为它们达到了一个返回语句。

无论如何,在第二个示例中,你展示了使用匿名闭包的好方法。传递给goroutine的chan可以在其他地方返回,并在线程之间实现安全的消息传递:

c := make(chan string)
go func() {
    for {
        select {
        case s := <-input1:
            c <- s
        case s := <-input2:
            c <- s
        }
    }
}()
return c

有几种方法可以结束匿名goroutine中的for循环,包括在第二个chan上进行select,使用关闭通道,当通道被close()时可以返回。此外,通常可以使用WaitGroup来实现这一点。

英文:

The for statements loops forever, and so the &lt;-c chan is never passed along and the chans get filled, but the main thread gets stuck waiting for c := fanInNew(a, b).

fanInNew() never returns because for loops forever (and blocks on select btw):

func fanInNew(input1, input2 &lt;-chan string) &lt;-chan string {
    c := make(chan string)
    for { // Loop forever and read from inputs 1 and 2
        select {
        case s := &lt;-input1:
            c &lt;- s
        case s := &lt;-input2:
            c &lt;- s            
        }
    }
    return c
}

Then in the main thread this function never returns the c chan.

func main() {
    // Never gets passed the next line
    c := fanInNew(boring(&quot;joe&quot;), boring(&quot;anh&quot;))
}

So you can put the for loops themselves in goroutines, as you did in the second example. Also typically goroutines should return, either because you pass a message in (such as by close()ing), or because they reach a return statement.

In any case, what you have in the 2nd example is great for demonstrating the use of anonymous closures. The chan passed into the goroutine can be returned elsewhere and enable safe message passing between threads:

c := make(chan string)
go func() {
    for {
        select {
        case s := &lt;-input1:
            c &lt;- s
        case s := &lt;-input2:
            c &lt;- s
        }
    }
}()
return c

There are a few ways of ending a for loop in an anonymous goroutine, including select on a second chan, a closing channel, which when close()ed you can return. Also, typically WaitGroups can achieve that.

huangapple
  • 本文由 发表于 2017年1月15日 02:50:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/41653695.html
匿名

发表评论

匿名网友

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

确定