go - 可以使用很多匿名函数吗?

标签 go coding-style

<分区>

我喜欢 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
}

基本上,我将它包装到匿名 block func 中。

这样的用法很常见吗?其他地鼠会滥用这个事实吗?

编辑:澄清

好吧,看来我没解释好, 我想实现两件事

  1. 我想实现的是尽可能短的锁定互斥锁

    func() {
        s.Lock()
        defer s.Unlock()
        // don't lock too long
    
        // there is other code here, this func is not an empty func
    }()
    
  2. 这个函数里面不止一个http.Get,比方说在调用了example.com/api之后我要调用example.com/api2。我们需要尽快关闭 resp.Body,因此只与该服务器建立一个 TCP 连接。据我所知,如果还有另一个 HTTP 连接尚未关闭(resp.Body.Close() 未在先前的响应中调用),http.Get 将创建另一个 TCP 连接。

编辑 2:更多说明

第一个和最后一个匿名函数和锁是为了同步缓存。我是基于map[string]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()

最佳答案

您观察到 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()
}

回答原来的问题:不,我不认为这是常见的(具有多个内联匿名函数)。如果感觉不对,几乎可以肯定有更好的方法。

关于go - 可以使用很多匿名函数吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50597261/

相关文章:

google-app-engine - Google App Engine Go SDK 模板更新问题

google-app-engine - 托管虚拟机中的 Websocket 支持

python - 识别 C 项目中所有变量的类型

scala - 有没有办法在没有命名空间污染的情况下创建方法级常量?

c# - 明确地初始化构造函数中私有(private)字段的默认值..WTF?

ios - #define 或 const 字符串*

php - 链接函数调用时返回行的 PHP 标准

go - 将 byte slice 转换为 int slice

go - 如何在golang中解码查询参数

go - 在 Go 中使用输入和输出 channel