>  기사  >  백엔드 개발  >  이 프로그램이 왜 정지되나요?

이 프로그램이 왜 정지되나요?

PHPz
PHPz앞으로
2024-02-14 15:06:081082검색

이 프로그램이 왜 정지되나요?

PHP 편집자 Xigua는 프로그래밍 과정에서 프로그램이 중단되는 문제에 자주 직면합니다. 프로그램 정지는 프로그램이 실행 중에 오류 메시지 없이 갑자기 응답을 멈추는 것을 의미합니다. 이러한 상황은 종종 사람들을 무엇이 잘못되었는지 혼란스럽게 만듭니다. 이 프로그램이 정확히 왜 중단되나요? 이 문서에서는 프로그램 중단의 몇 가지 일반적인 원인을 살펴보고 문제 해결에 도움이 되는 솔루션을 제공합니다. 초보자이시든, 숙련된 개발자이시든, 이 내용이 여러분에게 도움이 될 것이라고 믿습니다.

질문 내용

가서 채널간 통신할 수 있는 코드가 있습니다. 원하는 대로 수행되는 것처럼 보이지만 결국에는 멈춥니다. 왜 멈추는지 진단하려고 합니다.

코드는 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을 통해 콘텐츠를 읽고 계십니다. 이 코드는 채널이 닫힐 때까지 종료되지 않습니다. 이는 이 코드 블록 이후에 발생합니다.

으아악

그래서 프로그램은 절대 종료되지 않습니다. 왜냐하면 이 모든 로직이 동일한 고루틴에 있기 때문입니다.

프로그램이 종료되기 전에 모든 레코드가 처리되도록 수정하고 확인하려면 채널 읽기 코드를 기본 고루틴에 유지하고 대기/닫기 로직을 ​​자체 고루틴에 넣으세요.

으아악

루프의 모든 하위 고루틴을 최종 기다리도록 편집 range - 대기 그룹을 사용하는 더 깔끔한 방법이 있을 수 있지만 빠른 수정 방법은 다음과 같습니다.

으아악

위 내용은 이 프로그램이 왜 정지되나요?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 stackoverflow.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제