Golang中goroutine代码问题排查与解决
Golang中goroutine代码问题排查与解决 以下是我的代码:
package main
import (
"bufio"
"fmt"
"net/http"
"time"
)
const (
numberOfWorkers int = 3
numberOfTasks int = 5
)
func worker(id int, url chan string, result chan bool){
client := http.Client{
Timeout: 5 * time.Second,
}
for receivedURL := range url{
fmt.Printf("worker %d start on %s\n", id, receivedURL)
resp, err := client.Get(receivedURL)
if err != nil {
resp.Body.Close()
panic(err)
}
fmt.Println("response status:", resp.Status)
scanner := bufio.NewScanner(resp.Body)
for i := 0; scanner.Scan() && i < 5; i++ {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
resp.Body.Close()
panic(err)
}
fmt.Printf("worker %d Done with %s\n\n\n\n", id, receivedURL)
resp.Body.Close()
result <- true
}
}
func main() {
url := make(chan string)
result := make(chan bool)
urls := []string{
"http://gobyexample.com",
"http://att.com",
"http://domaintools.com",
"http://microsoft.com",
"http://google.com",
}
for i := 0; i < numberOfWorkers; i++ {
go worker(i, url, result)
}
for j := 0; j < numberOfTasks; j++ {
url <- urls[j]
}
close(url)
for t := 0; t < numberOfTasks; t++ {
if <-result {
fmt.Printf("get %d done", t)
}
}
}
以下是输出:它卡住了(我认为是死锁)
dth,minimum-scale=1,initial-scale=1" class="next-head"/><meta charSet="utf-8" class="next-head"/><meta charSet="utf-8" class="rennaissanceAEM64 next-head"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" class="next-head"/>
<meta name="viewport" content="initial-scale=1.0, width=device-width" class="next-head"/>
<meta name="keywords" content="att, at&t, wireless, internet, tv, support, directv, phones, deals, prepaid, entertainment, bundles" class="next-head"/>
<meta name="description" content="Visit att.com to switch and save on phone plans, internet service, & TV with premium entertainment! America's best network is also the fastest." class="next-head"/>
worker 0 Done with http://att.com
response status: 200 OK
<!DOCTYPE html>
<html lang="en">
<head>
worker 1 Done with http://domaintools.com
我无法复制完整的输出,但基本上三个工作者完成了前三个任务,但没有人能执行 result <- true,然后就出现了死锁。
这是什么原因造成的?
更多关于Golang中goroutine代码问题排查与解决的实战教程也可以访问 https://www.itying.com/category-94-b0.html
2 回复
我们昨天讨论过这个问题。由于所有通道都是无缓冲的,当所有goroutine都开始向result发送数据时,就没有读取器可用,因为main函数仍在尝试向jobs发送数据…
更多关于Golang中goroutine代码问题排查与解决的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
问题出现在当HTTP请求发生错误时,resp.Body.Close() 在 panic(err) 之前被调用,但 panic 会导致goroutine立即终止,而后续的 result <- true 不会执行。这会导致主goroutine在等待 result 通道的接收时发生死锁,因为发送到 result 通道的次数少于 numberOfTasks。
以下是修复后的代码:
package main
import (
"bufio"
"fmt"
"net/http"
"time"
)
const (
numberOfWorkers int = 3
numberOfTasks int = 5
)
func worker(id int, url chan string, result chan bool) {
client := http.Client{
Timeout: 5 * time.Second,
}
for receivedURL := range url {
fmt.Printf("worker %d start on %s\n", id, receivedURL)
resp, err := client.Get(receivedURL)
if err != nil {
fmt.Printf("worker %d error on %s: %v\n", id, receivedURL, err)
result <- false
continue
}
fmt.Println("response status:", resp.Status)
scanner := bufio.NewScanner(resp.Body)
for i := 0; scanner.Scan() && i < 5; i++ {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Printf("worker %d scan error on %s: %v\n", id, receivedURL, err)
resp.Body.Close()
result <- false
continue
}
fmt.Printf("worker %d Done with %s\n\n", id, receivedURL)
resp.Body.Close()
result <- true
}
}
func main() {
url := make(chan string)
result := make(chan bool)
urls := []string{
"http://gobyexample.com",
"http://att.com",
"http://domaintools.com",
"http://microsoft.com",
"http://google.com",
}
for i := 0; i < numberOfWorkers; i++ {
go worker(i, url, result)
}
for j := 0; j < numberOfTasks; j++ {
url <- urls[j]
}
close(url)
for t := 0; t < numberOfTasks; t++ {
<-result
fmt.Printf("get %d done\n", t)
}
}
主要修改:
- 将
panic(err)替换为错误处理和向result通道发送false - 使用
continue跳过当前迭代的剩余部分,确保每个URL处理都会向result通道发送一个值 - 在错误情况下也向
result通道发送值,保证主goroutine不会死锁
这样无论请求成功还是失败,每个worker都会为每个处理的URL向 result 通道发送一个值,主goroutine能够接收到所有 numberOfTasks 个结果。

