为什么 `sync.WaitGroup` 无法完成?

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

why `sync.WaitGroup` can't finish?

问题

这段代码中的问题是在使用通道(channel)时没有关闭通道。在getUrl函数中,通过result <- res.Data.Link将结果发送到通道中,但在主函数中没有关闭通道。因此,在for link := range result这一行代码中,程序会一直等待通道中的数据,导致无法继续执行后面的代码。

要解决这个问题,可以在主函数的最后关闭通道,可以使用close(result)来关闭通道。这样,当所有的结果都被接收后,for link := range result这一行代码就会退出循环,程序可以继续执行后面的代码。修改后的代码如下:

// ...

wg.Wait()
close(result) // 关闭通道

for link := range result {
    fmt.Println(link)
}
log.Println("Done!")

这样修改后,代码就可以正常执行了。

英文:

Here is my code:

package main

import (
	&quot;bytes&quot;
	&quot;crypto/md5&quot;
	&quot;encoding/hex&quot;
	&quot;encoding/json&quot;
	&quot;fmt&quot;
	&quot;io/ioutil&quot;
	&quot;log&quot;
	&quot;net/http&quot;
	&quot;runtime&quot;
	&quot;sync&quot;
)

type Data struct {
	Link string `json:&quot;url&quot;`
}
type Result struct {
	Code uint32
	Msg  string `json:&quot;msg&quot;`
	Data Data   `json:&quot;data&quot;`
}

const (
	URL     = &quot;http://qiye.wxsdc.ediankai.com/api/v1/suppliers/1/staff/1/box/get&quot;
	SIGNKEY = &quot;i5OqMrNXVyOJ5GEMYoEtRHqN1P9ghk6I&quot;
	DATA_ID = &quot;2965612126&quot;
	EQU_ID  = &quot;1482806063&quot;
)

func getMD5Hash(text string) string {
	hasher := md5.New()
	hasher.Write([]byte(text))
	return hex.EncodeToString(hasher.Sum(nil))
}

func getUrl(payload []byte, wg *sync.WaitGroup, result chan string) {
	req, err := http.NewRequest(&quot;POST&quot;, URL, bytes.NewBuffer(payload))
	req.Header.Set(&quot;Content-Type&quot;, &quot;application/json&quot;)

	client := &amp;http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}
	var res Result
	json.Unmarshal(body, &amp;res)
	log.Println(res.Data.Link)
	result &lt;- res.Data.Link
	wg.Add(-1)
}
func main() {
	parameterStr := fmt.Sprintf(&quot;%vdata_id%vequ_id%v%v&quot;, SIGNKEY, DATA_ID, EQU_ID, SIGNKEY)
	log.Println(parameterStr)
	sign := getMD5Hash(parameterStr)
	log.Println(sign)
	var payload map[string]string = make(map[string]string)
	payload[&quot;equ_id&quot;] = EQU_ID
	payload[&quot;data_id&quot;] = DATA_ID
	payload[&quot;sign&quot;] = sign

	payloadJson, err := json.Marshal(payload)
	if err != nil {
		log.Fatalln(&quot;convet paylod failed!&quot;)
	}
	log.Println(string(payloadJson))

	runtime.GOMAXPROCS(runtime.NumCPU())
	var wg sync.WaitGroup
	result := make(chan string)
	for i := 0; i &lt; 10; i++ {
		wg.Add(1)
		go getUrl(payloadJson, &amp;wg, result)
	}
	wg.Wait()
	for link := range result {
		fmt.Println(link)
	}
	log.Println(&quot;Done!&quot;)
}

But:

for link := range result {
	fmt.Println(link)
}
log.Println(&quot;Done!&quot;)

can't be executed, what's the reason?

答案1

得分: 1

你需要关闭结果通道,这样从中读取的for循环在完成时就会被中断。为此,你可以将最后一部分重写为:

var wg sync.WaitGroup
result := make(chan string)
go func() {
    for link := range result {
        fmt.Println(link)
    }
}()
for i := 0; i < 10; i++ {
    wg.Add(1)
    go getUrl(payloadJson, &wg, result)
}
wg.Wait()
close(result)
英文:

You need to close the result channel so the for loop which reads from it is interrupted when its finished. For that you could rewrite the last part as:

var wg sync.WaitGroup
result := make(chan string)
go func() {
    for link := range result {
        fmt.Println(link)
    }
}()
for i := 0; i &lt; 10; i++ {
    wg.Add(1)
    go getUrl(payloadJson, &amp;wg, result)
}
wg.Wait()
close(result)

答案2

得分: 0

你的for循环永远不会停止。当所有的getUrl完成后,它会继续等待result。尝试这样做:

wg.Wait()

close(result)

for link := range result {
    fmt.Println(link)
}
log.Println("Done!")

如果你想打印结果,可以这样做:

package main

import (
    "bytes"
    "crypto/md5"
    "encoding/hex"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "runtime"
    "sync"
)

type Data struct {
    Link string `json:"url"`
}
type Result struct {
    Code uint32
    Msg  string `json:"msg"`
    Data Data   `json:"data"`
}

const (
    URL     = "http://qiye.wxsdc.ediankai.com/api/v1/suppliers/1/staff/1/box/get"
    SIGNKEY = "i5OqMrNXVyOJ5GEMYoEtRHqN1P9ghk6I"
    DATA_ID = "2965612126"
    EQU_ID  = "1482806063"
)

func getMD5Hash(text string) string {
    hasher := md5.New()
    hasher.Write([]byte(text))
    return hex.EncodeToString(hasher.Sum(nil))
}

func getUrl(payload []byte, wg *sync.WaitGroup, result chan string) {
    defer func(){
        wg.Done()
    }();

    req, err := http.NewRequest("POST", URL, bytes.NewBuffer(payload))
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return
    }
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return
    }
    var res Result
    json.Unmarshal(body, &res)
    log.Println(res.Data.Link)
    result <- res.Data.Link
}

func monitor(wg *sync.WaitGroup, cs chan string) {
    wg.Wait()
    close(cs)
}
func printResult(result <-chan string, done chan<- bool) {
    for i := range result {
        fmt.Println(i)
    }

    done <- true
}

func main() {
    parameterStr := fmt.Sprintf("%vdata_id%vequ_id%v%v", SIGNKEY, DATA_ID, EQU_ID, SIGNKEY)
    log.Println(parameterStr)
    sign := getMD5Hash(parameterStr)
    log.Println(sign)
    var payload map[string]string = make(map[string]string)
    payload["equ_id"] = EQU_ID
    payload["data_id"] = DATA_ID
    payload["sign"] = sign

    payloadJson, err := json.Marshal(payload)
    if err != nil {
        log.Fatalln("convet paylod failed!")
    }
    log.Println(string(payloadJson))

    runtime.GOMAXPROCS(runtime.NumCPU())
    var wg sync.WaitGroup
    result := make(chan string)
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go getUrl(payloadJson, &wg, result)
    }
    
    go monitor(&wg, result)

    done := make(chan bool, 1)
    go printResult(result, done)
    <-done
    log.Println("Done!")
}
英文:

Your for loop never stop. When all the getUrl finish, it continues to wait on result. Try this:

wg.Wait()
close(result)
for link := range result {
fmt.Println(link)
}
log.Println(&quot;Done!&quot;)

If you want to print the result, you could do like:

package main
import (
&quot;bytes&quot;
&quot;crypto/md5&quot;
&quot;encoding/hex&quot;
&quot;encoding/json&quot;
&quot;fmt&quot;
&quot;io/ioutil&quot;
&quot;log&quot;
&quot;net/http&quot;
&quot;runtime&quot;
&quot;sync&quot;
)
type Data struct {
Link string `json:&quot;url&quot;`
}
type Result struct {
Code uint32
Msg  string `json:&quot;msg&quot;`
Data Data   `json:&quot;data&quot;`
}
const (
URL     = &quot;http://qiye.wxsdc.ediankai.com/api/v1/suppliers/1/staff/1/box/get&quot;
SIGNKEY = &quot;i5OqMrNXVyOJ5GEMYoEtRHqN1P9ghk6I&quot;
DATA_ID = &quot;2965612126&quot;
EQU_ID  = &quot;1482806063&quot;
)
func getMD5Hash(text string) string {
hasher := md5.New()
hasher.Write([]byte(text))
return hex.EncodeToString(hasher.Sum(nil))
}
func getUrl(payload []byte, wg *sync.WaitGroup, result chan string) {
defer func(){
wg.Done()
}();
req, err := http.NewRequest(&quot;POST&quot;, URL, bytes.NewBuffer(payload))
req.Header.Set(&quot;Content-Type&quot;, &quot;application/json&quot;)
client := &amp;http.Client{}
resp, err := client.Do(req)
if err != nil {
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return
}
var res Result
json.Unmarshal(body, &amp;res)
log.Println(res.Data.Link)
result &lt;- res.Data.Link
}
func monitor(wg *sync.WaitGroup, cs chan string) {
wg.Wait()
close(cs)
}
func printResult(result &lt;-chan string, done chan&lt;- bool) {
for i := range result {
fmt.Println(i)
}
done &lt;- true
}
func main() {
parameterStr := fmt.Sprintf(&quot;%vdata_id%vequ_id%v%v&quot;, SIGNKEY, DATA_ID, EQU_ID, SIGNKEY)
log.Println(parameterStr)
sign := getMD5Hash(parameterStr)
log.Println(sign)
var payload map[string]string = make(map[string]string)
payload[&quot;equ_id&quot;] = EQU_ID
payload[&quot;data_id&quot;] = DATA_ID
payload[&quot;sign&quot;] = sign
payloadJson, err := json.Marshal(payload)
if err != nil {
log.Fatalln(&quot;convet paylod failed!&quot;)
}
log.Println(string(payloadJson))
runtime.GOMAXPROCS(runtime.NumCPU())
var wg sync.WaitGroup
result := make(chan string)
for i := 0; i &lt; 10; i++ {
wg.Add(1)
go getUrl(payloadJson, &amp;wg, result)
}
go monitor(&amp;wg, result)
done := make(chan bool, 1)
go printResult(result, done)
&lt;-done
log.Println(&quot;Done!&quot;)
}

huangapple
  • 本文由 发表于 2016年12月28日 14:12:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/41356356.html
匿名

发表评论

匿名网友

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

确定