Maison > développement back-end > Golang > Pourquoi ce programme se bloque-t-il ?

Pourquoi ce programme se bloque-t-il ?

PHPz
Libérer: 2024-02-14 15:06:08
avant
1120 Les gens l'ont consulté

Pourquoi ce programme se bloque-t-il ?

L'éditeur PHP Xigua rencontre souvent le problème du blocage du programme pendant le processus de programmation. Le blocage du programme signifie que le programme cesse soudainement de répondre pendant l'exécution sans aucun message d'erreur. Cette situation laisse souvent les gens confus quant à ce qui ne va pas. Pourquoi exactement ce programme se bloque-t-il ? Dans cet article, nous explorerons certaines causes courantes de blocage du programme et proposerons des solutions pour aider à résoudre le problème. Que vous soyez débutant ou développeur expérimenté, je pense que ces contenus peuvent vous être utiles.

Contenu de la question

J'ai du code pour communiquer entre les chaînes en cours. Il semble faire ce qu'il veut, mais se bloque à la fin. J'essaie de diagnostiquer pourquoi il se bloque.

Le code utilise httpbin.org pour obtenir un uuid aléatoire, puis le publier tout en respectant les limites de concurrence et de débit que j'ai établies via le canal sémaphore et le canal tarifaire.

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
}
Copier après la connexion

Solution de contournement

Vous lisez du contenu via range 语句从代码末尾的 respchan. Ce code ne sort que lorsque le canal est fermé – ce qui se produit après ce bloc de code.

for ele := range respchan {
    // ...
}


wg.wait()        
close(respchan)
Copier après la connexion

Donc, le programme ne se termine jamais - parce que toute cette logique est dans la même goroutine.

Pour corriger et garantir que tous les enregistrements sont traités avant la fin du programme, conservez le code de lecture du canal dans la goroutine principale et placez la logique d'attente/fermeture dans sa propre goroutine :

go func() {
    wg.wait()        // wait for workers to finish ...
    close(respchan)  // ... now signal the main goroutine we're done
}()

for ele := range respchan {
    // ...
}
Copier après la connexion

Modifier pour attendre la finale range toute goroutine enfant dans la boucle - il existe probablement un moyen plus propre d'utiliser simplement un groupe d'attente, mais une solution rapide pourrait être :

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()
        // ...
    }()
}
Copier après la connexion

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:stackoverflow.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal