php小編西瓜在程式設計過程中,常會遇到程式掛起的問題。程式掛起是指程式在執行過程中突然停止回應,且沒有任何錯誤提示。這種情況常常讓人感到困惑,不知道出了什麼問題。究竟為什麼這個程式掛起?在本文中,我們將探討一些常見的程式掛起原因,並提供解決方案來幫助解決這個問題。無論你是初學者還是有經驗的開發者,相信這些內容都能對你有幫助。
我有在 go 中的通道之間進行通訊的程式碼。它似乎完成了所需的操作,但最後掛起。我正在嘗試診斷它為何掛起。
程式碼使用 httpbin.org
取得隨機 uuid,然後將其發布,同時遵守我透過信號量通道和速率通道建立的並發和速率限制。
package main import ( "bytes" "encoding/json" "fmt" "io" "net/http" "sync" "time" ) type HttpBinGetRequest struct { url string } type HttpBinGetResponse struct { Uuid string `json:"uuid"` StatusCode int } type HttpBinPostRequest struct { url string uuid string // Item to post to API } type HttpBinPostResponse struct { Data string `json:"data"` StatusCode int } func main() { // Prepare GET requests for n requests var requests []*HttpBinGetRequest for i := 0; i < 10; i++ { uri := "https://httpbin.org/uuid" request := &HttpBinGetRequest{ url: uri, } requests = append(requests, request) } // Create semaphore and rate limit for the GET endpoint getSemaphore := make(chan struct{}, 10) getRate := make(chan struct{}, 10) defer close(getRate) defer close(getSemaphore) for i := 0; i < cap(getRate); i++ { getRate <- struct{}{} } go func() { // ticker corresponding to 1/nth of a second // where n = rate limit // basically (1000 / rps) * time.Millisecond ticker := time.NewTicker(100 * time.Millisecond) defer ticker.Stop() for range ticker.C { _, ok := <-getRate if !ok { return } } }() // Send our GET requests to obtain a random UUID respChan := make(chan HttpBinGetResponse) var wg sync.WaitGroup for _, request := range requests { wg.Add(1) // cnt := c // Go func to make request and receive the response go func(r *HttpBinGetRequest) { defer wg.Done() // Check the rate limiter and block if it is empty getRate <- struct{}{} // fmt.Printf("Request #%d at: %s\n", cnt, time.Now().UTC().Format("2006-01-02T15:04:05.000Z07:00")) resp, _ := get(r, getSemaphore) fmt.Printf("%+v\n", resp) // Place our response into the channel respChan <- *resp // fmt.Printf("%+v,%s\n", resp, time.Now().UTC().Format("2006-01-02T15:04:05.000Z07:00")) }(request) } // Set up for POST requests 10/s postSemaphore := make(chan struct{}, 10) postRate := make(chan struct{}, 10) defer close(postRate) defer close(postSemaphore) for i := 0; i < cap(postRate); i++ { postRate <- struct{}{} } go func() { // ticker corresponding to 1/nth of a second // where n = rate limit // basically (1000 / rps) * time.Millisecond ticker := time.NewTicker(100 * time.Millisecond) defer ticker.Stop() for range ticker.C { _, ok := <-postRate if !ok { return } } }() // Read responses as they become available for ele := range respChan { postReq := &HttpBinPostRequest{ url: "https://httpbin.org/post", uuid: ele.Uuid, } go func(r *HttpBinPostRequest) { postRate <- struct{}{} postResp, err := post(r, postSemaphore) if err != nil { fmt.Println(err) } fmt.Printf("%+v\n", postResp) }(postReq) } wg.Wait() close(respChan) } func get(hbgr *HttpBinGetRequest, sem chan struct{}) (*HttpBinGetResponse, error) { // Add a token to the semaphore sem <- struct{}{} // Remove token when function is complete defer func() { <-sem }() httpResp := &HttpBinGetResponse{} client := &http.Client{} req, err := http.NewRequest("GET", hbgr.url, nil) if err != nil { fmt.Println("error making request") return httpResp, err } req.Header = http.Header{ "accept": {"application/json"}, } resp, err := client.Do(req) if err != nil { fmt.Println(err) fmt.Println("error getting response") return httpResp, err } // Read Response body, err := io.ReadAll(resp.Body) if err != nil { fmt.Println("error reading response body") return httpResp, err } json.Unmarshal(body, &httpResp) httpResp.StatusCode = resp.StatusCode return httpResp, nil } // Method to post data to httpbin func post(hbr *HttpBinPostRequest, sem chan struct{}) (*HttpBinPostResponse, error) { // Add a token to the semaphore sem <- struct{}{} defer func() { <-sem }() httpResp := &HttpBinPostResponse{} client := &http.Client{} req, err := http.NewRequest("POST", hbr.url, bytes.NewBuffer([]byte(hbr.uuid))) if err != nil { fmt.Println("error making request") return httpResp, err } req.Header = http.Header{ "accept": {"application/json"}, } resp, err := client.Do(req) if err != nil { fmt.Println("error getting response") return httpResp, err } // Read Response body, err := io.ReadAll(resp.Body) if err != nil { fmt.Println("error reading response body") return httpResp, err } json.Unmarshal(body, &httpResp) httpResp.StatusCode = resp.StatusCode return httpResp, nil }
您正在透過 range
語句從程式碼末尾的 respchan
讀取內容。在通道關閉之前,此程式碼不會退出 - 這發生在該程式碼區塊之後。
for ele := range respchan { // ... } wg.wait() close(respchan)
所以程式永遠不會退出 - 因為所有這些邏輯都在同一個 goroutine 中。
要修復並確保在程式退出之前處理所有記錄,請將通道讀取程式碼保留在主 goroutine 中,並將等待/關閉邏輯放入其自己的 goroutine 中:
go func() { wg.wait() // wait for workers to finish ... close(respchan) // ... now signal the main goroutine we're done }() for ele := range respchan { // ... }
編輯以等待最終range
循環中的任何子goroutine - 可能有一種更簡潔的方法來僅使用一個等待群組,但一個快速修復可能是:
var swg sync.WaitGroup go func() { wg.Wait() // wait for workers to finish ... swg.Wait() // ... and sub-tasks close(respChan) // ... now signal the main goroutine we're done }() for ele := range respChan { // ... swg.Add(1) go func() { defer swg.Done() // ... }() }
以上是為什麼這個程式掛起?的詳細內容。更多資訊請關注PHP中文網其他相關文章!