【问题标题】:How to create elements of a struct with a Mutex field如何使用 Mutex 字段创建结构的元素
【发布时间】:2016-08-12 19:55:17
【问题描述】:

我有一个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(),则可能会发生数据竞争。

我有一个如何解决这个问题的计划。就是使用互斥锁。为此,我将在 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

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

【问题讨论】:

  • 尝试用sync.Mutex替换sync.Mutex(){}
  • 这似乎完全错误,甚至比旧代码更生动。不要将互斥锁放在您的结构中,将其保持在读取状态并同步对res 的访问。也许更好:摆脱命名的返回参数并返回来自 Get 的值或您的错误标记。也许更好:将响应从 Get 传输到通过通道读取。
  • 对命名返回 res 的访问是不同步的,因此很活泼。
  • 在 go 例程中写入 res 是不同步的。
  • 即使解决了这个问题,这也是让 http 请求超时的错误方法。设置 http Client.Timeout。您没有提供任何方法来取消正在进行的请求,这可能永远不会释放其资源。

标签: rest http go timeout mutex


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    我听从了沃尔克的建议,通过渠道解决了问题。

    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) 的返回值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-12
      • 2021-01-04
      • 1970-01-01
      • 1970-01-01
      • 2020-08-26
      • 1970-01-01
      相关资源
      最近更新 更多