我有一个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
在这种情况下使用互斥锁的正确方法是什么?
答案 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)
的返回值。