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

时间:2016-04-20 08:58:25

标签: rest http go timeout mutex

我有一个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}
}

以及Read()功能:

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\n", 504}

    }
    return
}

函数返回的Response类型定义为:

type Response struct {
    Body       string
    StatusCode int
}

此读取函数使用Get()函数并实现超时。问题是如果发生超时并且Get()响应在res中同时写入Read(),则可能会发生数据争用。

我有一个如何解决这个问题的计划。这是使用Mutex。为此,我将向Response结构添加一个字段:

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

以便可以锁定Response。但是,我不知道如何在代码的其他部分修复此问题。

对于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.
}

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\n", 504} // And mutex was added here.

    }
    defer res.mu.Unlock()
    return
}

此“解决方案”会产生以下错误:

./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\n" (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

在这种情况下使用互斥锁的正确方法是什么?

2 个答案:

答案 0 :(得分:2)

虽然您对Volker指导的回答很好,但您可能需要考虑使用非默认http.Client,以便您可以在发出请求的客户端上设置Timeout(那么您没有担心自己处理超时。)

答案 1 :(得分:1)

我遵循了Volker的建议并使用了一个渠道来解决问题。

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\n", 504} 
    }
    return
}

现在,不能同时写入res。它将是超时或Get(url)的返回值。