Der PHP-Editor Xigua stößt häufig auf das Problem, dass das Programm während des Programmiervorgangs hängen bleibt. Programmabsturz bedeutet, dass das Programm während der Ausführung plötzlich nicht mehr reagiert, ohne dass eine Fehlermeldung erscheint. Diese Situation verwirrt die Menschen oft darüber, was falsch ist. Warum genau hängt dieses Programm? In diesem Artikel untersuchen wir einige häufige Ursachen für Programmabstürze und stellen Lösungen zur Behebung des Problems bereit. Egal, ob Sie Anfänger oder erfahrener Entwickler sind, ich glaube, dass diese Inhalte für Sie hilfreich sein können.
Ich habe Code für die Kommunikation zwischen Kanälen in Go. Es scheint zu tun, was es will, bleibt aber am Ende hängen. Ich versuche zu diagnostizieren, warum es hängt.
Der Code verwendet httpbin.org
, um eine zufällige UUID abzurufen und sie dann zu veröffentlichen, während die Parallelitäts- und Ratenbeschränkungen eingehalten werden, die ich über den Semaphorkanal und den Ratenkanal festgelegt habe.
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 }
Sie lesen Inhalte über range
语句从代码末尾的 respchan
. Dieser Code wird erst beendet, wenn der Kanal geschlossen wird – was nach diesem Codeblock geschieht.
for ele := range respchan { // ... } wg.wait() close(respchan)
Das Programm wird also nie beendet – denn all diese Logik befindet sich in derselben Goroutine.
Um das Problem zu beheben und sicherzustellen, dass alle Datensätze verarbeitet werden, bevor das Programm beendet wird, belassen Sie den Kanallesecode in der Haupt-Goroutine und fügen Sie die Warte-/Schließlogik in die eigene Goroutine ein:
go func() { wg.wait() // wait for workers to finish ... close(respchan) // ... now signal the main goroutine we're done }() for ele := range respchan { // ... }
Bearbeiten um auf das Finale zu warten range
jede untergeordnete Goroutine in der Schleife – es gibt wahrscheinlich eine sauberere Möglichkeit, einfach eine Wartegruppe zu verwenden, aber eine schnelle Lösung könnte sein:
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() // ... }() }
Das obige ist der detaillierte Inhalt vonWarum hängt dieses Programm?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!