Golang程序在执行过程中卡住,无法完成执行。

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

Golang program hangs without finishing execution

问题

我有以下的 Golang 程序:

package main

import (
	"fmt"
	"net/http"
	"time"
)

var urls = []string{
	"http://www.google.com/",
	"http://golang.org/",
	"http://yahoo.com/",
}

type HttpResponse struct {
	url      string
	response *http.Response
	err      error
	status   string
}

func asyncHttpGets(url string, ch chan *HttpResponse) {
	client := http.Client{}
	if url == "http://www.google.com/" {
		time.Sleep(500 * time.Millisecond) //google is down
	}

	fmt.Printf("Fetching %s \n", url)
	resp, err := client.Get(url)
	u := &HttpResponse{url, resp, err, "fetched"}
	ch <- u
	fmt.Println("sent to chan")
}

func main() {
	fmt.Println("start")
	ch := make(chan *HttpResponse, len(urls))
	for _, url := range urls {
		go asyncHttpGets(url, ch)
	}

	for i := range ch {
		fmt.Println(i)
	}
	fmt.Println("Im done")
}

然而,当我运行它时,它会挂起(即最后应该打印 Im done 的部分没有运行)。
以下是终端输出:

$ go run get.go
start
Fetching http://yahoo.com/
Fetching http://golang.org/
Fetching http://www.google.com/
sent to chan
&{http://www.google.com/ 0xc820144120 <nil> fetched}
sent to chan
&{http://golang.org/ 0xc82008b710 <nil> fetched}
sent to chan
&{http://yahoo.com/ 0xc82008b7a0 <nil> fetched}

你可以在 Playground 上运行它

英文:

I have the following golang program;

package main

import (
	&quot;fmt&quot;
	&quot;net/http&quot;
	&quot;time&quot;
)

var urls = []string{
	&quot;http://www.google.com/&quot;,
	&quot;http://golang.org/&quot;,
	&quot;http://yahoo.com/&quot;,
}

type HttpResponse struct {
	url      string
	response *http.Response
	err      error
	status   string
}

func asyncHttpGets(url string, ch chan *HttpResponse) {
	client := http.Client{}
	if url == &quot;http://www.google.com/&quot; {
		time.Sleep(500 * time.Millisecond) //google is down
	}

	fmt.Printf(&quot;Fetching %s \n&quot;, url)
	resp, err := client.Get(url)
	u := &amp;HttpResponse{url, resp, err, &quot;fetched&quot;}
	ch &lt;- u
	fmt.Println(&quot;sent to chan&quot;)
}

func main() {
	fmt.Println(&quot;start&quot;)
	ch := make(chan *HttpResponse, len(urls))
	for _, url := range urls {
		go asyncHttpGets(url, ch)
	}

	for i := range ch {
		fmt.Println(i)
	}
	fmt.Println(&quot;Im done&quot;)

}

<kbd>Run it on Playground</kbd>

However when I run it; it hangs (ie the last part that ought to print Im done doesnt run.)
Here's the terminal output;;
$ go run get.go
start
Fetching http://yahoo.com/
Fetching http://golang.org/
Fetching http://www.google.com/
sent to chan
&{http://www.google.com/ 0xc820144120 <nil> fetched}
sent to chan
&{http://golang.org/ 0xc82008b710 <nil> fetched}
sent to chan
&{http://yahoo.com/ 0xc82008b7a0 <nil> fetched}

答案1

得分: 5

问题在于,在for循环中遍历一个通道,除非通道关闭,否则会一直进行下去。如果你想从通道中精确地读取len(urls)个值,你应该循环这么多次:

for i := 0; i < len(urls); i++ {
    fmt.Println(<-ch)
}
英文:

The problem is that ranging over a channel in a for loop will continue forever unless the channel is closed. If you want to read precisely len(urls) values from the channel, you should loop that many times:

for i := 0; i &lt; len(urls); i++ {
    fmt.Println(&lt;-ch)
}

答案2

得分: 0

另一个好的、肮脏的、狡猾的技巧是使用sync.WaitGroup,并在每个goroutine中递增它,然后使用Wait来监视它,当它完成时,它将关闭你的通道,允许下一块代码运行。我提供这种方法的原因是它避免了在循环中使用静态数字(如len(urls)),这样你可以拥有一个可能会改变的动态切片。

Waitclose在它们自己的goroutine中是为了让你的代码能够到达for loop,以便在通道上进行range操作。

package main

import (
    "fmt"
    "net/http"
    "time"
    "sync"
)

var urls = []string{
    "http://www.google.com/",
    "http://golang.org/",
    "http://yahoo.com/",
}

type HttpResponse struct {
    url      string
    response *http.Response
    err      error
    status   string
}

func asyncHttpGets(url string, ch chan *HttpResponse, wg *sync.WaitGroup) {
    client := http.Client{}
    if url == "http://www.google.com/" {
        time.Sleep(500 * time.Millisecond) //google is down
    }

    fmt.Printf("Fetching %s \n", url)
    resp, err := client.Get(url)
    u := &HttpResponse{url, resp, err, "fetched"}
    ch <- u
    fmt.Println("sent to chan")
    wg.Done()
}

func main() {
    fmt.Println("start")
    ch := make(chan *HttpResponse, len(urls))
    var wg sync.WaitGroup
    for _, url := range urls {
        wg.Add(1)
        go asyncHttpGets(url, ch, &wg)
    }

    go func() {
        wg.Wait()
        close(ch)
    }()

    for i := range ch {
        fmt.Println(i)
    }
    fmt.Println("Im done")
}
英文:

Another good dirty devious trick would be to use sync.WaitGroup and increment it per goroutine and then monitor it with a Wait and after its done it will close your channel allowing the next blocks of code to run, the reason I am offering you this approach is because it gets away from using a static number in a loop like len(urls) so that you can have a dynamic slice that might change and what not.

The reason Wait and close are in their own goroutine is so that your code can reach the for loop to range over your channel

package main
import (
&quot;fmt&quot;
&quot;net/http&quot;
&quot;time&quot;
&quot;sync&quot;
)
var urls = []string{
&quot;http://www.google.com/&quot;,
&quot;http://golang.org/&quot;,
&quot;http://yahoo.com/&quot;,
}
type HttpResponse struct {
url      string
response *http.Response
err      error
status   string
}
func asyncHttpGets(url string, ch chan *HttpResponse, wg *sync.WaitGroup) {
client := http.Client{}
if url == &quot;http://www.google.com/&quot; {
time.Sleep(500 * time.Millisecond) //google is down
}
fmt.Printf(&quot;Fetching %s \n&quot;, url)
resp, err := client.Get(url)
u := &amp;HttpResponse{url, resp, err, &quot;fetched&quot;}
ch &lt;- u
fmt.Println(&quot;sent to chan&quot;)
wg.Done()
}
func main() {
fmt.Println(&quot;start&quot;)
ch := make(chan *HttpResponse, len(urls))
var wg sync.WaitGroup
for _, url := range urls {
wg.Add(1)
go asyncHttpGets(url, ch, &amp;wg)
}
go func() {
wg.Wait()
close(ch)
}()
for i := range ch {
fmt.Println(i)
}
fmt.Println(&quot;Im done&quot;)
}

huangapple
  • 本文由 发表于 2015年12月6日 01:54:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/34108862.html
匿名

发表评论

匿名网友

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

确定