проблемы тупиковой ситуации + увеличено количество подпрограмм при работе с каналом - PullRequest
0 голосов
/ 09 февраля 2020

Я работаю над простой программой проверки статуса http с Go. Сначала программа прошла нормально. Затем я ввел channel в это, это просто не останавливается. Другими словами, WorkGroup.Done() не запускается.

как я могу вызвать WorkGroup.Done()?

// go playground: https://play.golang.org/p/1AzXEAV9p4K
package main

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

type urlState struct {
    url   string
    state bool
}

func checkLink(link string, c chan<- urlState) {
    _, err := http.Get(link)
    if err != nil {
        fmt.Println(link, `is not working`)
        c <- urlState{
            url:   link,
            state: false,
        }
    } else {
        fmt.Println(link, `is working`)
        c <- urlState{
            url:   link,
            state: true,
        }
    }
}

func Check(links []string) bool {
    result := true
    stats := make(map[string]bool)
    var wg sync.WaitGroup
    fmt.Println(`total sites: `, len(links))
    wg.Add(len(links)) // add workgroups of exactly same amount as links array length

    c := make(chan urlState, len(links))
    for _, link := range links {
        stats[link] = false
        fmt.Println(`requesting...`, link)
        go checkLink(link, c)
        fmt.Println(`goroutines: `, runtime.NumGoroutine())
    }

    for v := range c {
        fmt.Println(`recv from channel, assigning result for `, v.url)
        stats[v.url] = v.state
        wg.Done()
        fmt.Println(`goroutines: `, runtime.NumGoroutine())
    }

    wg.Wait() // any codes below are not reached. increased number of goroutines seem to be the reason. 
    close(c)
    fmt.Println(`work finished!`)
    for key, value := range stats {
        fmt.Println(key, `---`, value)
        if value == false {
            result = false
        }
    }
    return result
}

func main() {
  links := []string{`https://goolgle.com`, `https://amazon.com`} // two goroutines are expected
  Check(links) // shows increased number of goroutines. and it goes deadlock
}

1 Ответ

1 голос
/ 09 февраля 2020

Программа блокируется на for v := range c, потому что диапазон на канале продолжается до тех пор, пока канал не будет закрыт. Исправьте, получив ожидаемое количество значений. WaitGroup не требуется с этим изменением.

result := true
stats := make(map[string]bool)
fmt.Println(`total sites: `, len(links))

c := make(chan urlState, len(links))
for _, link := range links {
    stats[link] = false
    fmt.Println(`requesting...`, link)
    go checkLink(link, c)
    fmt.Println(`goroutines: `, runtime.NumGoroutine())
}

for i := 0; i < len(links); i++ {
    v := <-c
    fmt.Println(`recv from channel, assigning result for `, v.url)
    stats[v.url] = v.state
    fmt.Println(`goroutines: `, runtime.NumGoroutine())
}

fmt.Println(`work finished!`)
for key, value := range stats {
    fmt.Println(key, `---`, value)
    if value == false {
        result = false
    }
}

Запустите его на игровой площадке

Другой способ решить проблему - закрыть c после checkLink Горутины завершены. Используйте WaitGroup, чтобы координировать это.

func checkLink(link string, c chan<- urlState, wg *sync.WaitGroup) {
    defer wg.Done()
    ... remainder of function is same as before
}

func Check(links []string) bool {
    result := true
    stats := make(map[string]bool)
    var wg sync.WaitGroup
    fmt.Println(`total sites: `, len(links))
    wg.Add(len(links)) // add workgroups of exactly same amount as links array length

    c := make(chan urlState, len(links))
    for _, link := range links {
        stats[link] = false
        fmt.Println(`requesting...`, link)
        go checkLink(link, c, &wg)
        fmt.Println(`goroutines: `, runtime.NumGoroutine())
    }

    // Close c when checkLink goroutines complete.
    go func() {
        wg.Wait()
        close(c)
    }()

    for v := range c {
        fmt.Println(`recv from channel, assigning result for `, v.url)
        stats[v.url] = v.state
        wg.Done()
        fmt.Println(`goroutines: `, runtime.NumGoroutine())
    }

    fmt.Println(`work finished!`)
    for key, value := range stats {
        fmt.Println(key, `---`, value)
        if value == false {
            result = false
        }
    }
    return result
}

Запустите его на детской площадке

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...