我喜欢golang的一件事是defer
声明,但defer
仅适用于func
范围。
所以,我经常像这样使用它
func (s *Something) abc() error {
func() {
s.Lock()
defer s.Unlock()
// don't lock too long
}()
// do something else
if err := func() error {
resp, err := http.Get("https://example.com/api")
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return errors.New("Failed to download")
}
var tmp struct {
Error bool `json:"error"`
Result string `json:"result"`
}
if err := json.NewDecoder(resp.Body).Decode(&tmp); err != nil {
return err
}
if tmp.Error {
return errors.New("API return error")
}
s.somedata = tmp.result
return nil
}(); err != nil {
return err
}
func() {
s.Lock()
defer s.Unlock()
// don't lock too long
}()
// do something else
}
基本上,我把它包装成匿名块func
。
使用这样的东西会很常见吗?任何其他地鼠都滥用这个事实吗?
编辑:澄清
好吧,看起来我没解释得很好, 我想实现两件事
我想要实现的是尽可能缩短互斥锁
func() {
s.Lock()
defer s.Unlock()
// don't lock too long
// there is other code here, this func is not an empty func
}()
在这个函数中有多个http.Get,让我们说在调用example.com/api后我需要调用example.com/api2。我们需要尽快关闭resp.Body,因此只有一个TCP连接到此服务器。 AFAIK,http.Get将创建另一个TCP连接,如果还有另一个尚未关闭的HTTP连接(在之前的响应中没有调用resp.Body.Close())。
编辑2:更多澄清
第一个和最后一个匿名函数和锁是同步缓存。我基于map [string]字符串实现缓存,因此需要进行同步。
我首先需要调用example.com/api,并根据我需要调用example.com/api2或example.com/api3的响应,当时必须关闭之前的http连接,它可以是代码像这样
resp, err := http.Get("https://example.com/api")
if err != nil {
return err
}
if resp.StatusCode != 200 {
resp.Body.Close()
return errors.New("Failed to download")
}
// process the body
resp.Body.Close()
但你需要两次显式写resp.Body.Close()
答案 0 :(得分:2)
您认为defer
仅在函数范围内有效,我认为您遇到的问题与defer
的行为无关。
一般来说,[插入意见]最好有更小的功能,那么你就不需要创建匿名函数来最大限度地使用defer
。
拆分你的例子,这样的事情可能会更好:
// Public method that does locking orchestration
func (s *Something) PublicDoABC() error {
s.doWorkStart()
if err := s.populateSomeData(); err != nil {
return err
}
s.doWorkEnd()
return nil
}
// setup function with locking
func (s *Something) doWorkStart() {
s.Lock()
defer s.Unlock()
// do setup work here
}
// teardown function with locking
func (s *Something) doWorkEnd() {
s.Lock()
defer s.Unlock()
// do teardown work here
}
// do the actual request
func (s *Something) populateSomeData() error {
resp, err := http.Get("https://example.com/api")
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return errors.New("Failed to download")
}
var tmp struct {
Error bool `json:"error"`
Result string `json:"result"`
}
if err := json.NewDecoder(resp.Body).Decode(&tmp); err != nil {
return err
}
if tmp.Error {
return errors.New("API return error")
}
s.somedata = tmp.Result
return nil
}
我知道这会改变一些事情,你可能不想拆分函数的主体,你可以在Something
上定义一个允许使用锁定执行任意函数的方法:
func (s *Something) PublicDoABC() error {
s.executeLocked(func() {
// do setup work
})
if err := s.populateSomeData(); err != nil {
return err
}
s.executeLocked(func() {
// do teardown work
})
return nil
}
// this function allows defer unlocks and unifies locking code
func (s *Something) executeLocked(f func()) {
s.Lock()
defer s.Unlock()
f()
}
回答原始问题:不,我不相信这是常见的(具有多个内联功能)。如果感觉不对,那几乎可以肯定是一种更好的方法。
答案 1 :(得分:0)
由于您对Lock
和Unlock
的匿名功能,您将输入匿名函数,锁定,延迟解锁,让匿名函数触发延迟解锁,然后继续{{ 1}}。开头和结尾的匿名函数几乎没用。
您的中间匿名函数对于任何语言都是糟糕的风格。没有理由不做代码并回复任何返回的错误。
我会按如下方式重写此函数:
abc()
匿名功能有其自己的位置。我已将它们用于封闭以及非常简洁的常规操作。您可以在此处使用匿名函数的一个地方是记录func (s *Something) abc() error {
s.Lock()
defer s.Unlock()
resp, err := http.Get("https://example.com/api")
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return errors.New("Failed to download")
}
// This struct should probably be global
var tmp struct {
Error bool `json:"error"`
Result string `json:"result"`
}
err = json.NewDecoder(resp.Body).Decode(&tmp)
if err != nil {
return err
}
if tmp.Error {
return errors.New("API return error")
}
s.somedata = tmp.result
return nil
}
返回的错误,如下所示:
resp.Body.Close()