Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/go/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在goroutine中找出数据竞争的问题_Go_Concurrency_Goroutine - Fatal编程技术网

在goroutine中找出数据竞争的问题

在goroutine中找出数据竞争的问题,go,concurrency,goroutine,Go,Concurrency,Goroutine,我最近开始学习围棋,我已经花了一段时间在这方面,但我想是时候寻求一些具体的帮助了。我的程序从api请求分页数据,因为有大约160页的数据。似乎是goroutines的一个很好的用法,除了我有比赛条件,我似乎不知道为什么。这可能是因为我不熟悉这种语言,但我的印象是,函数的参数在调用它的函数中作为数据的副本传递,除非它是指针 据我所知,这应该是复制我的数据,让我可以在主功能中自由更改它,但我最终多次请求某些页面,而其他页面仅请求一次 我的主要任务是去 package main import (

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

据我所知,这应该是复制我的数据,让我可以在主功能中自由更改它,但我最终多次请求某些页面,而其他页面仅请求一次

我的主要任务是去

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()

}

func getRequestURL(baseURL string, filters map[string]string) *url.URL {
    requestURL, err := url.Parse(baseURL)
    if err != nil {
        log.Fatalln(err)
    }

    query := requestURL.Query()
    for key, value := range filters {
        query.Set(key, value)
    }
    requestURL.RawQuery = query.Encode()

    return requestURL
}

func requestData(url *url.URL, httpClient *http.Client) CollegeScoreCardResponseDTO {
    request, _ := http.NewRequest(http.MethodGet, url.String(), nil)

    resp, err := httpClient.Do(request)
    if err != nil {
        log.Fatalln(err)
    }
    defer resp.Body.Close()

    var parsedResponse CollegeScoreCardResponseDTO
    err = json.NewDecoder(resp.Body).Decode(&parsedResponse)
    if err != nil {
        log.Fatalln(err)
    }

    return parsedResponse
}

主程序包
进口(
“布菲奥”
“编码/json”
“日志”
“net/http”
“网络/网址”
“操作系统”
“strconv”
“同步”
“github.com/joho/godotenv”
)
func main(){
错误:=godotenv.Load()
如果错误!=零{
log.Fatalln(错误)
}
httpClient:=&http.Client{}
baseURL:=”https://api.data.gov/ed/collegescorecard/v1/schools.json"
过滤器:=make(映射[字符串]字符串)
页码:=0
过滤器[“学校授予的学位”]=“2,3”
过滤器[“字段”]=“id,学校。名称,学校。城市,2018年。学生。规模,2017年。学生。规模,2017年。收入。完成后3年。总体贫困线,2016年。还款。3年还款。总体”
过滤器[“api_键”]=os.Getenv(“api_键”)
outFile,err:=os.Create(“./out.txt”)
如果错误!=零{
log.Fatalln(错误)
}
writer:=bufio.NewWriter(outFile)
requestURL:=getRequestURL(baseURL,筛选器)
响应:=请求数据(请求URL,httpClient)
wg:=sync.WaitGroup{}
对于(第+1页)*response.Metadata.ResultsPerPage
我知道我将遇到的另一个问题是以正确的顺序写入输出文件,但我相信使用通道告诉每个例程完成了什么请求可以解决这个问题。如果我在这一点上不正确,我也会很感激任何关于如何处理这一问题的建议


提前感谢。

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

我建议您保持
过滤器
只读,通过复制
过滤器
中的所有项目,在goroutine中创建一个新映射,并在goroutine中添加
“页面”
。您还必须小心传递
页面的副本

go func(page int) {
   flt:=make(map[string]string)
   for k,v:=range filters {
     flt[k]=v
   }
   flt["page"]=strconv.Itoa(page)
   ...
} (page)

您也不能同时写入
编写器
。Go类型对于并发使用是不安全的,除非有文档记录。