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

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

问题描述:

Here is my code:

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) {
    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 {
        panic(err)
    }
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        panic(err)
    }
    var res Result
    json.Unmarshal(body, &res)
    log.Println(res.Data.Link)
    result <- res.Data.Link
    wg.Add(-1)
}
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)
    }
    wg.Wait()
    for link := range result {
        fmt.Println(link)
    }
    log.Println("Done!")
}

But:

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

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

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 < 10; i++ {
    wg.Add(1)
    go getUrl(payloadJson, &wg, result)
}
wg.Wait()
close(result)

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("Done!")

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

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!")
}