为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
慕妹3146593 2022-08-01 19:10:38
我最近开始学习围棋,我已经在这个问题上花了一段时间了,但我想是时候寻求一些具体的帮助了。我的程序从API请求分页数据,因为大约有160页的数据。这似乎是对goroutines的一个很好的使用,除了我有比赛条件,我似乎不知道为什么。这可能是因为我是该语言的新手,但我的印象是,除非它是指针,否则函数的参数将作为调用它的函数中的数据副本传递。根据我认为我知道的,这应该是复制我的数据,这让我可以自由地在主函数中更改它,但我最终多次请求一些页面,而其他页面只请求一次。我的主.gopackage mainimport (    "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()}我知道我将遇到的另一个问题是以正确的顺序写入输出文件,但我相信使用通道告诉每个例程完成写入的请求可以解决这个问题。如果我在这一点上不正确,我将不胜感激关于如何解决这个问题的任何建议。
查看完整描述

1 回答

?
慕仙森

TA贡献1827条经验 获得超8个赞

goroutines不接收数据的副本。当编译器检测到变量“转义”当前函数时,它会在堆上分配该变量。在这种情况下,就是这样一个变量。当 goroutine 启动时,它访问的映射与主线程相同。由于您在主线程中不断修改而不锁定,因此无法保证 goroutine 看到的内容。filtersfiltersfilters


我建议你保持只读,通过复制中的所有项目在goroutine中创建新地图,然后在goroutine中添加。您还必须小心传递的副本:filtersfilters"page"page


go func(page int) {

   flt:=make(map[string]string)

   for k,v:=range filters {

     flt[k]=v

   }

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

   ...

} (page)


查看完整回答
反对 回复 2022-08-01
  • 1 回答
  • 0 关注
  • 203 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号