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

Is it okay to use many anonymous functions?

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

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

编辑:澄清

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

  1. 我想实现的是尽可能短的锁住mutex

    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 连接尚未关闭,http.Get 将创建另一个 TCP 连接(resp.Body.Close() 未在先前的响应中调用)。

编辑 2:更多说明

第一个和最后一个匿名函数和锁是为了同步缓存。我是基于map[string]string实现缓存的,所以需要sync-ed.

我需要先调用 example.com/api,然后根据响应我需要调用 example.com/api2 或 example.com/api3,然后再调用 previous 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()

由于您对 LockUnlock 的匿名函数,您将进入匿名函数、锁定、延迟解锁、离开触发延迟解锁的匿名函数,然后继续 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
}

匿名函数有它们的位置。我将它们用于闭包以及极其简洁的 go 例程。您可能会在此处使用匿名函数的一个地方是记录 resp.Body.Close() 返回的错误,如下所示:

defer func() {
    err = resp.Body.Close()
    log.Printf("error closing API response body: %s", err)
}()

你的观察是正确的 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()
}

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