死锁问题+使用通道时增加goroutine的数量
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了死锁问题+使用通道时增加goroutine的数量相关的知识,希望对你有一定的参考价值。
我正在使用Go开发一个简单的http状态检查程序。该程序起初运行良好。然后,我向其中引入了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
}
答案
程序在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
}
以上是关于死锁问题+使用通道时增加goroutine的数量的主要内容,如果未能解决你的问题,请参考以下文章