关于Go中的频道指示和阻止的混淆

时间:2017-09-29 02:29:52

标签: go concurrency goroutine

在函数定义中,如果某个通道是没有方向的参数,它是否必须发送或接收某些内容?

func makeRequest(url string, ch chan<- string, results chan<- string) {
    start := time.Now()

    resp, err := http.Get(url)
    defer resp.Body.Close()
    if err != nil {
        fmt.Printf("%v", err)
    }

    resp, err = http.Post(url, "text/plain", bytes.NewBuffer([]byte("Hey")))
    defer resp.Body.Close()

    secs := time.Since(start).Seconds()

    if err != nil {
        fmt.Printf("%v", err)
    }
    // Cannot move past this.
    ch <- fmt.Sprintf("%f", secs) 
    results <- <- ch
}

func MakeRequestHelper(url string, ch chan string, results chan string, iterations int) {
    for i := 0; i < iterations; i++ {
        makeRequest(url, ch, results)
    }
    for i := 0; i < iterations; i++ {
        fmt.Println(<-ch)
    }
}

func main() {
    args := os.Args[1:]
    threadString := args[0]
    iterationString := args[1]
    url := args[2]
    threads, err := strconv.Atoi(threadString)
    if err != nil {
        fmt.Printf("%v", err)
    }
    iterations, err := strconv.Atoi(iterationString)
    if err != nil {
        fmt.Printf("%v", err)
    }

    channels := make([]chan string, 100)
    for i := range channels {
        channels[i] = make(chan string)
    }

    // results aggregate all the things received by channels in all goroutines
    results := make(chan string, iterations*threads)

    for i := 0; i < threads; i++ {
        go MakeRequestHelper(url, channels[i], results, iterations)

    }

    resultSlice := make([]string, threads*iterations)
    for i := 0; i < threads*iterations; i++ {
        resultSlice[i] = <-results
    }
}

在上面的代码中,

  

ch&lt; - 或&lt; -results

似乎阻止了执行makeRequest的每个goroutine。

我是Go的并发模型的新手。我理解发送到通道块并从通道块接收但发现很难阻止此代码中的内容。

2 个答案:

答案 0 :(得分:2)

channels中的频道为nil(不执行make;您制作片段但不制作频道),因此任何发送或接收都将被阻止。我不确定你到底想要做什么,但这是基本问题。

有关渠道如何运作的说明,请参阅https://golang.org/doc/effective_go.html#channels

答案 1 :(得分:2)

我不确定你在做什么......这看起来真的很复杂。我建议你阅读如何使用频道。

https://tour.golang.org/concurrency/2

据说你在代码中有这么多事情,只是简单地把它变得更容易了。 (可以进一步简化)。我留下了评论来理解代码。

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "sync"
    "time"
)

// using structs is a nice way to organize your code
type Worker struct {
    wg        sync.WaitGroup
    semaphore chan struct{}
    result    chan Result
    client    http.Client
}

// group returns so that you don't have to send to many channels
type Result struct {
    duration float64
    results  string
}

// closing your channels will stop the for loop in main
func (w *Worker) Close() {
    close(w.semaphore)
    close(w.result)
}

func (w *Worker) MakeRequest(url string) {
    // a semaphore is a simple way to rate limit the amount of goroutines running at any single point of time
    // google them, Go uses them often
    w.semaphore <- struct{}{}
    defer func() {
        w.wg.Done()
        <-w.semaphore
    }()

    start := time.Now()

    resp, err := w.client.Get(url)
    if err != nil {
        log.Println("error", err)
        return
    }
    defer resp.Body.Close()

    // don't have any examples where I need to also POST anything but the point should be made
    // resp, err = http.Post(url, "text/plain", bytes.NewBuffer([]byte("Hey")))
    // if err != nil {
    // log.Println("error", err)
    // return
    // }
    // defer resp.Body.Close()

    secs := time.Since(start).Seconds()

    b, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Println("error", err)
        return
    }
    w.result <- Result{duration: secs, results: string(b)}
}

func main() {
    urls := []string{"https://facebook.com/", "https://twitter.com/", "https://google.com/", "https://youtube.com/", "https://linkedin.com/", "https://wordpress.org/",
        "https://instagram.com/", "https://pinterest.com/", "https://wikipedia.org/", "https://wordpress.com/", "https://blogspot.com/", "https://apple.com/",
    }

    workerNumber := 5
    worker := Worker{
        semaphore: make(chan struct{}, workerNumber),
        result:    make(chan Result),
        client:    http.Client{Timeout: 5 * time.Second},
    }

    // use sync groups to allow your code to wait for
    // all your goroutines to finish
    for _, url := range urls {
        worker.wg.Add(1)
        go worker.MakeRequest(url)
    }

    // by declaring wait and close as a seperate goroutine
    // I can get to the for loop below and iterate on the results
    // in a non blocking fashion
    go func() {
        worker.wg.Wait()
        worker.Close()
    }()

    // do something with the results channel
    for res := range worker.result {
        fmt.Printf("Request took %2.f seconds.\nResults: %s\n\n", res.duration, res.results)
    }
}
相关问题