不知道 strings.Fields(string) 这个函数。不错。
更多关于Golang中解决死锁错误的常见方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
谢谢。它在 Playground 上无法运行,因为那里禁止所有互联网请求。正如你所说,它在本地可以正常工作。
我在循环前添加了一个 fmt.Printf,发现 sites 中的第一个 v 是空的。这意味着你向等待组添加了 6 个计数,但只调用了 5 次 Done。我建议改为这样做:
func main() {
fmt.Println("hello world")
}
@skillian @Unique 另外,我认为像这里 https://play.golang.org/p/uoPsczEUXon 这样使用 sites := strings.Fields(b.String()) 会更好。
它在 Playground 中会抛出一个讨厌的错误,但在本地 IDE 中一切正常。
func main() {
fmt.Println("hello world")
}
您也可以在迭代之前从切片中过滤掉空字符串,像这样 https://play.golang.org/p/RcTQomu2j7d (1)
仍然建议像skillian所做的那样添加到WaitGroup中。
(1) 由于未知原因,我的示例在playgroup上无法运行,但在本地使用go版本go1.14 windows/amd64可以正常工作。
在Golang中解决死锁错误,关键在于确保所有goroutine都能正确完成且channel操作保持平衡。根据你提供的代码,主要问题在于channel使用不当导致goroutine阻塞。
问题分析
你的代码存在以下问题:
- 无缓冲channel在发送操作后会阻塞,直到有接收操作
wg.Wait()在goroutine完成前被调用,但goroutine被channel阻塞无法完成- 缺少独立的goroutine来接收channel数据
修正方案
方案1:使用带缓冲的channel
package main
import (
"fmt"
"net/http"
"sync"
)
func fetchStatus(wg *sync.WaitGroup, url string, ch chan string) {
defer wg.Done()
resp, err := http.Get(url)
if err != nil {
ch <- fmt.Sprintf("%s: %v", url, err)
return
}
ch <- fmt.Sprintf("%s: %d", url, resp.StatusCode)
}
func main() {
urls := []string{
"https://golang.org",
"https://google.com",
"https://github.com",
}
ch := make(chan string, len(urls)) // 使用带缓冲的channel
var wg sync.WaitGroup
for _, url := range urls {
wg.Add(1)
go fetchStatus(&wg, url, ch)
}
wg.Wait()
close(ch)
for result := range ch {
fmt.Println(result)
}
}
方案2:使用独立的接收goroutine
package main
import (
"fmt"
"net/http"
"sync"
)
func fetchStatus(wg *sync.WaitGroup, url string, ch chan string) {
defer wg.Done()
resp, err := http.Get(url)
if err != nil {
ch <- fmt.Sprintf("%s: %v", url, err)
return
}
ch <- fmt.Sprintf("%s: %d", url, resp.StatusCode)
}
func main() {
urls := []string{
"https://golang.org",
"https://google.com",
"https://github.com",
}
ch := make(chan string)
var wg sync.WaitGroup
// 启动接收goroutine
go func() {
wg.Wait()
close(ch)
}()
for _, url := range urls {
wg.Add(1)
go fetchStatus(&wg, url, ch)
}
// 在主goroutine中接收结果
for result := range ch {
fmt.Println(result)
}
}
方案3:使用select避免阻塞(推荐)
package main
import (
"fmt"
"net/http"
"sync"
)
func fetchStatus(wg *sync.WaitGroup, url string, ch chan string) {
defer wg.Done()
resp, err := http.Get(url)
if err != nil {
ch <- fmt.Sprintf("%s: %v", url, err)
return
}
ch <- fmt.Sprintf("%s: %d", url, resp.StatusCode)
}
func main() {
urls := []string{
"https://golang.org",
"https://google.com",
"https://github.com",
}
ch := make(chan string)
var wg sync.WaitGroup
done := make(chan bool)
// 启动接收goroutine
go func() {
for result := range ch {
fmt.Println(result)
}
done <- true
}()
for _, url := range urls {
wg.Add(1)
go fetchStatus(&wg, url, ch)
}
// 等待所有goroutine完成
go func() {
wg.Wait()
close(ch)
}()
<-done // 等待接收完成
}
关键要点
- channel容量:无缓冲channel需要发送和接收同时就绪,否则会阻塞
- goroutine生命周期:确保所有goroutine都能正常退出
- WaitGroup使用:
wg.Wait()应在所有wg.Add()调用之后 - channel关闭:发送完成后及时关闭channel,避免接收方永久阻塞
这些修正方案都能有效解决死锁问题,方案3提供了更好的并发控制和资源管理。

