Golang并发程序中的Bug排查与解决方法
Golang并发程序中的Bug排查与解决方法 我想让 api 和 handle 这两个函数并发工作,但程序运行不正常,我该如何修复它?我还需要说明,我不能将 api 函数移到 handle 函数的循环中(因为在主项目中,handle 函数是 net/http 标准库的一部分)。
package main
func api() {
println("api")
}
func handle() {
for {
println("handle")
}
}
func main() {
for {
go api()
go handle()
}
}
请告诉我,我的做法是否完全错误!
更多关于Golang并发程序中的Bug排查与解决方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html
我知道这会很快耗尽内存,但主程序必须永远这样做!
实际上,这个项目变得非常奇怪。总的来说,我想做以下事情: 首先,端点及其服务必须可用 其次,在服务可用的同时处理一个API
这就是为什么我想到了并发,但它变得非常困难。
我认为将 main 函数改为以下形式会更有益处:
func main() {
go workWithApi()
handleRequests()
}
这样可以在独立的 goroutine 中启动 workWithApi,然后让主 goroutine 运行 http.ListenAndServe。
你想做什么?
按照程序现在的写法,api 会打印 "api" 然后返回,所以即使你不断地生成 api 协程,它们最终还是会终止。另一方面,handle 自身运行着一个无限循环,所以 main 函数中 for 循环的每一次迭代都会产生一个新的、打印 "handle" 且永远不会返回的 goroutine,因此这个程序会很快耗尽你的所有内存。
我想,我会把这个问题看作更像是:
package main
func api() {
println("api")
http.ListenAndServe(http.HandlerFunc(handle))
}
func handle(w http.ResponseWriter, r *http.Request) {
println("handle")
}
func main() {
// TODO: 当有更多后台任务需要从main启动时,将其放入一个goroutine中。
// 我们暂时保持它是一个普通的函数调用,这样main函数就不会返回。
api()
}
我们需要更多关于您尝试实现的目标的信息,以便提供更好的反馈。您目前的代码无法工作,并且似乎没有任何实际用途:您不能在一个没有同步机制的无限 for 循环中启动 goroutine。这与做类似下面的事情是一样的:
package main
func main() {
var vs []int
for {
vs = append(vs, 0)
}
}
它会尽可能多地分配内存,然后导致 panic。让您的 main 函数在无限 for 循环中启动 goroutine 也是一样的;这不会产生任何有用的结果。
我们能否获得更多关于这个 API 的信息?当我想到 API 时,我想到的是一个等待请求到来并在请求到来时进行处理的服务。您使用这个 api 的方式在我看来更像是一个工作进程:它启动,执行一些工作(例如 println("api")),然后返回。然而,对于工作进程,通常会有一些同步机制来限制 goroutine 的数量,否则它们会耗尽您计算机上的所有资源。
我们绝对需要更多关于 handle 函数目的的信息。您不能在一个无限循环中启动 goroutine,而这些 goroutine 本身又处于永不停止的无限循环中;这与我上面提到的向切片追加元素是一样的:唯一的结果就是内存耗尽。
我认为主项目的代码能更好地表达我的意思。
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"github.com/mtarif98/go-gateio/database"
"github.com/mtarif98/go-gateio/json"
)
func getJson() []byte {
resp, err := http.Get("https://data.gateapi.io/api2/1/tickers")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
bytes, _ := ioutil.ReadAll(resp.Body)
return bytes
}
func handleCreate(w http.ResponseWriter, req *http.Request) {
data := gjson.DecodeJson(req)
fmt.Println(data)
}
func workWithApi() {
data := getJson()
var config database.DBData
database.LoadDBData(&config)
targetDB := fmt.Sprintf(
"%s:%s@tcp(%s:%s)/%s",
config.DBUsername,
config.DBPassword,
config.DBAddress,
config.DBPort,
config.DBName,
)
cryptosFiltered := gjson.GetEssentialInformation(data)
database.CreateRecords(cryptosFiltered, targetDB) // for now CreateRecords function write a simple log - TEMPORARY
}
func handleRequests() {
http.HandleFunc("/api/v1/user/create/", handleCreate)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func main() { // 这里我希望 handleRequests() 和 workWithApi 能并发工作,我的意思是,当程序在Web上运行时,它同时也在与API交互。
handleRequests() // 处理请求
workWithApi() // 处理API
}
你的代码中存在两个主要问题:无限循环和缺乏并发控制机制。以下是修复后的版本:
package main
import (
"sync"
"time"
)
func api() {
println("api")
}
func handle() {
for {
println("handle")
time.Sleep(1 * time.Second) // 避免CPU占用过高
}
}
func main() {
var wg sync.WaitGroup
// 启动handle协程(只需要一个)
wg.Add(1)
go func() {
defer wg.Done()
handle()
}()
// 在主循环中启动api协程
for i := 0; i < 5; i++ { // 限制循环次数,避免无限创建协程
wg.Add(1)
go func() {
defer wg.Done()
api()
}()
time.Sleep(500 * time.Millisecond) // 控制api调用频率
}
wg.Wait()
}
或者更接近你原始结构的版本:
package main
import (
"sync"
"time"
)
func api() {
println("api")
}
func handle() {
for {
println("handle")
time.Sleep(1 * time.Second)
}
}
func main() {
var wg sync.WaitGroup
// 启动handle协程
wg.Add(1)
go func() {
defer wg.Done()
handle()
}()
// 使用select控制主循环
ticker := time.NewTicker(500 * time.Millisecond)
defer ticker.Stop()
for i := 0; i < 5; i++ {
<-ticker.C
wg.Add(1)
go func() {
defer wg.Done()
api()
}()
}
wg.Wait()
}
关键修复点:
- 使用
sync.WaitGroup确保协程正确同步 - 为无限循环添加延迟避免CPU爆满
- 控制协程创建数量,避免内存泄漏
handle()函数只需要启动一次,而不是在每次循环中都创建新协程

