在 goroutine 中难以弄清楚数据竞争

我最近开始学习围棋,我已经在这个问题上花了一段时间了,但我想是时候寻求一些具体的帮助了。我的程序从API请求分页数据,因为大约有160页的数据。这似乎是对goroutines的一个很好的使用,除了我有比赛条件,我似乎不知道为什么。这可能是因为我是该语言的新手,但我的印象是,除非它是指针,否则函数的参数将作为调用它的函数中的数据副本传递。


根据我认为我知道的,这应该是复制我的数据,这让我可以自由地在主函数中更改它,但我最终多次请求一些页面,而其他页面只请求一次。


我的主.go


package main


import (

    "bufio"

    "encoding/json"

    "log"

    "net/http"

    "net/url"

    "os"

    "strconv"

    "sync"


    "github.com/joho/godotenv"

)


func main() {

    err := godotenv.Load()

    if err != nil {

        log.Fatalln(err)

    }


    httpClient := &http.Client{}

    baseURL := "https://api.data.gov/ed/collegescorecard/v1/schools.json"


    filters := make(map[string]string)

    page := 0

    filters["school.degrees_awarded.predominant"] = "2,3"

    filters["fields"] = "id,school.name,school.city,2018.student.size,2017.student.size,2017.earnings.3_yrs_after_completion.overall_count_over_poverty_line,2016.repayment.3_yr_repayment.overall"

    filters["api_key"] = os.Getenv("API_KEY")


    outFile, err := os.Create("./out.txt")

    if err != nil {

        log.Fatalln(err)

    }

    writer := bufio.NewWriter(outFile)


    requestURL := getRequestURL(baseURL, filters)


    response := requestData(requestURL, httpClient)


    wg := sync.WaitGroup{}

    for (page+1)*response.Metadata.ResultsPerPage < response.Metadata.TotalResults {

        page++

        filters["page"] = strconv.Itoa(page)


        wg.Add(1)

        go func() {

            defer wg.Done()


            requestURL := getRequestURL(baseURL, filters)


            response := requestData(requestURL, httpClient)


            _, err = writer.WriteString(response.TextOutput())

            if err != nil {

                log.Fatalln(err)

            }

        }()


    }


    wg.Wait()


}


我知道我将遇到的另一个问题是以正确的顺序写入输出文件,但我相信使用通道告诉每个例程完成写入的请求可以解决这个问题。如果我在这一点上不正确,我将不胜感激关于如何解决这个问题的任何建议。


慕妹3146593
浏览 205回答 1
1回答

慕仙森

goroutines不接收数据的副本。当编译器检测到变量“转义”当前函数时,它会在堆上分配该变量。在这种情况下,就是这样一个变量。当 goroutine 启动时,它访问的映射与主线程相同。由于您在主线程中不断修改而不锁定,因此无法保证 goroutine 看到的内容。filtersfiltersfilters我建议你保持只读,通过复制中的所有项目在goroutine中创建新地图,然后在goroutine中添加。您还必须小心传递的副本:filtersfilters"page"pagego func(page int) {&nbsp; &nbsp;flt:=make(map[string]string)&nbsp; &nbsp;for k,v:=range filters {&nbsp; &nbsp; &nbsp;flt[k]=v&nbsp; &nbsp;}&nbsp; &nbsp;flt["page"]=strconv.Itoa(page)&nbsp; &nbsp;...} (page)
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go