如何使用Mutex字段创建结构的元素

I have a Get() function:

func Get(url string) *Response {
    res, err := http.Get(url)
    if err != nil {
        return &Response{}
    }
    // res.Body != nil when err == nil
    defer res.Body.Close()
    body, err := ioutil.ReadAll(res.Body)
    if err != nil {
        log.Fatalf("ReadAll: %v", err)
    }
    reflect.TypeOf(body)
    return &Response{sync.Mutex(),string(body), res.StatusCode}
}

as well as a Read() function:

func Read(url string, timeout time.Duration) (res *Response) { 
    done := make(chan bool) 

    go func() {
        res = Get(url)      
        done <- true        
    }()     
        select {    // As soon as either
    case <-done:    // done is sent on the channel or 
    case <-time.After(timeout): // timeout
        res = &Response{"Gateway timeout
", 504}

    }
    return
}

the Response type returned by the functions is defined as:

type Response struct {
    Body       string
    StatusCode int
}

This read function makes use of the Get() function and also implements a timeout. The problem is that a data race can occur if the timeout occurs and the Get() response is written to res at the same time in Read().

I have a plan for how to solve this. It is to use Mutex. To do this, I would add a field to the Response struct:

type Response struct {
    mu         sync.Mutex
    Body       string
    StatusCode int
}

so that the Response can be locked. However, I'm not sure how to fix this in the other parts of the code.

My attempt looks like this, for the Get():

func Get(url string) *Response {
    res, err := http.Get(url)
    if err != nil {
        return &Response{}
    }
    // res.Body != nil when err == nil
    defer res.Body.Close()
    body, err := ioutil.ReadAll(res.Body)
    if err != nil {
        log.Fatalf("ReadAll: %v", err)
    }
    reflect.TypeOf(body)
    return &Response{sync.Mutex(),string(body), res.StatusCode} // This line is changed.
}

and for the Read():

func Read(url string, timeout time.Duration) (res *Response) { 
    done := make(chan bool) 
    res = &Response{sync.Mutex()} // this line has been added

    go func() { 
        res = Get(url)      
        done <- true        
    }()     
        select {    
    case <-done:    
    case <-time.After(timeout):
        res.mu.Lock()
        res = &Response{sync.Mutex(), "Gateway timeout
", 504} // And mutex was added here.

    }
    defer res.mu.Unlock()
    return
}

This "solution" generates these errors:

./client.go:54: missing argument to conversion to sync.Mutex: sync.Mutex()
./client.go:63: missing argument to conversion to sync.Mutex: sync.Mutex()
./client.go:63: too few values in struct initializer
./client.go:73: missing argument to conversion to sync.Mutex: sync.Mutex()
./client.go:95: cannot use "Service unavailable
" (type string) as type sync.Mutex in field value
./client.go:95: cannot use 503 (type int) as type string in field value
./client.go:95: too few values in struct initializer

What is the correct way of using Mutex in this case?

I followed Volker's suggestion and used a channel to solve the problem.

func Read(url string, timeout time.Duration) (res *Response) { 
    done := make(chan bool) // A channel
    resChan := make(chan *Response)

    go func() { 
        resChan <- Get(url) 
        done <- true    
    }()     
    select {    
        case <-done:
            res = &Response{}    
        case <-time.After(timeout):
            res = &Response{"Gateway timeout
", 504} 
    }
    return
}

Now, there can be no simultaneous writes to res. It's going to be either the timeout or the returned value of Get(url).

While your answer with Volker's guidance is good, you might want to consider using a non default http.Client so that you can set a Timeout on the client making the request (then you don't have to worry about handling the timeouts yourself).