在这种情况下,我可以省略 defer 关键字吗?

huangapple go评论140阅读模式
英文:

can I omit defer keyword in this case?

问题

这两段代码是相同的吗?运行它们看起来是一样的,我见过两种写法。

英文:
go func() {
        defer wg.Done()
        for i := 0; i < n; i++ {
            ch <- i
        }
    }()
go func() {
        for i := 0; i < n; i++ {
            ch <- i
        }
        wg.Done()
    }()

Are these two code the same? Seems the same running them and I have seen both writings.

答案1

得分: 2

这两个看起来是相同的,但也可能存在它们不同的情况。这是因为如果函数发生恐慌,延迟函数也会被执行。

例如,如果ch是一个关闭的通道,在关闭的通道上发送数据会导致恐慌(参见https://stackoverflow.com/questions/39015602/how-does-a-non-initialized-channel-behave/39016004#39016004),如果没有使用deferwg.Done()将不会被调用。而使用了defer,它将会被调用。这对于另一个调用wg.Wait()的goroutine来说可能非常重要,以便解除阻塞。

另一个区别是,延迟函数及其参数在defer语句执行时进行求值,即在循环之前。如果在第一次发送后修改了wg变量,使用defer的函数将不会看到wg的新值,而不使用defer的函数将在循环之后进行求值,因此会看到新值。

总之,在合适的时候使用defer,即使你或其他人以后修改/扩展代码,也不会忘记调用。想象一个例子,其中有人插入了一个条件性的return

go func() {
    defer wg.Done()
    for i := 0; i < n; i++ {
        ch <- i
        if i == 3 {
            return
        }
    }
}()

使用defer,这不会成为一个问题,wg.Wait()仍然会被调用。如果没有使用defer,它将不会被调用。

参考相关问题:

https://stackoverflow.com/questions/45617758/proper-way-to-release-resources-with-defer-in-a-loop/45620423#45620423

https://stackoverflow.com/questions/51360229/the-deferred-calls-arguments-are-evaluated-immediately/51361500#51361500

英文:

Those 2 seem to be identical, there may be cases when they're not. That's because deferred functions are also executed if the function is panicking.

So for example if ch is a closed channel, sending on a closed channel will panic (see https://stackoverflow.com/questions/39015602/how-does-a-non-initialized-channel-behave/39016004#39016004), and without defer wg.Done() will not be called. With defer, it will be. This may be very important for another goroutine calling wg.Wait() to get unblocked.

Another difference is that the deferred function and its arguments are evaluated when the defer statement is executed, in your case before the loop. If the wg variable would be modified e.g. after the first send, the one with defer would not see the new value of wg, the one without defer would see that as it's evaluated after the loop.

All in all, use defer when it makes sense, and even if you or someone else later end up modifying / extending the code, it will not be forgotten. Just think of an example where someone inserts a conditional return:

go func() {
    defer wg.Done()
    for i := 0; i &lt; n; i++ {
        ch &lt;- i
        if i == 3 {
            return
        }
    }
}()

Using defer, this won't be a problem, wg.Wait() will still be called. Without defer, it won't be.

See related questions:

https://stackoverflow.com/questions/45617758/proper-way-to-release-resources-with-defer-in-a-loop/45620423#45620423

https://stackoverflow.com/questions/51360229/the-deferred-calls-arguments-are-evaluated-immediately/51361500#51361500

huangapple
  • 本文由 发表于 2022年7月7日 00:01:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/72886678.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定