延迟在围棋中的应用

defer在围棋中的用途是什么?语言档案编制表示当周围的函数返回时执行。为什么不把代码放在给定函数的末尾呢?

22660 次浏览

我们通常使用 defer来关闭或释放资源。

周围的函数在返回之前执行所有延迟的函数调用,即使它感到恐慌。如果您只是在周围函数的末尾放置一个函数调用,那么当恐慌发生时就会跳过该函数调用。

此外,延迟函数调用可以通过调用 recover内置函数来处理紧急情况。这不能通过函数末尾的普通函数调用来完成。

每个延迟调用放在堆栈上,并在周围函数结束时以相反的顺序执行。反向顺序有助于正确释放资源。

必须到达 defer语句才能调用函数。

您可以将其视为实现 try-catch-finally块的另一种方法。

try-finally那样收盘:

func main() {
f, err := os.Create("file")
if err != nil {
panic("cannot create file")
}
defer f.Close()
// no matter what happens here file will be closed
// for sake of simplicity I skip checking close result
fmt.Fprintf(f,"hello")
}

try-catch-finally一样关闭和紧急处理

func main() {
defer func() {
msg := recover()
fmt.Println(msg)
}()
f, err := os.Create(".") // . is a current directory
if err != nil {
panic("cannot create file")
}
defer f.Close()
// no matter what happens here file will be closed
// for sake of simplicity I skip checking close result
fmt.Fprintf(f,"hello")
}

相对于 try-catch-finally 的好处是不存在块和变量范围的嵌套。这简化了周围函数的结构。

与 finally 块一样,如果延迟函数调用可以到达返回的数据,它们也可以修改返回值。

func yes() (text string) {
defer func() {
text = "no"
}()
return "yes"
}


func main() {
fmt.Println(yes())
}

使用 defer的主要好处-无论函数如何返回,它都会被调用。如果发生特殊情况,将调用延迟函数。

因此,它提供了不错的东西:

  1. 恢复后 panic。这允许是实现 try ... catch的行为。

  2. 正常退出前不要忘记清理(关闭文件、释放内存等)。您可能会打开一些资源,并且在退出之前必须关闭它。但是函数可以有几个出口点-所以您必须在 每个返回点中添加释放。维护起来很枯燥。或者您只能放置一个延迟语句-资源将被自动释放。

当然,并不总是保证你的代码可以到达函数的末尾(例如,一个错误或者其他条件可能迫使你在函数结束之前返回)。Defer 语句确保无论分配给它的函数是什么,即使函数出现恐慌或者代码在函数结束之前返回,它也能确保执行。

Defer 语句还有助于保持代码的整洁(尤其是)。在函数(尤指)中有多个 return 语句的情况下。当需要在返回之前释放资源时(例如,假设在函数的开始部分有一个访问资源的 open 调用——为了避免资源泄漏,在函数返回之前必须调用相应的 close。假设你的函数有多个 return 语句,可能适用于不同的条件,包括错误检查。在这种情况下,如果没有延迟,您通常会在每个 return 语句之前为该资源调用 close)。Defer 语句确保您传递给它的函数始终被调用,而不管函数返回到哪里,从而避免了繁重的内部管理工作。

也可以在同一个函数中多次调用 Defer。例如: 如果你有不同的资源通过你的函数被分配,需要在返回之前最终释放,那么你可以在分配之后调用它们中的每一个,这些函数的执行顺序与它们在函数退出时被调用的顺序相反。

defer语句将函数的执行推迟到 函数返回。

这个例子演示了 defer的功能:

func elapsed(what string) func() {
start := time.Now()
fmt.Println("start")
return func() {
fmt.Printf("%s took %v\n", what, time.Since(start))
}
}


func main() {
defer elapsed("page")()
time.Sleep(time.Second * 3)
}

退出:

start
page took 3s

这里已经有了很好的答案,我想再提一个用例。

func BillCustomer(c *Customer) error {
c.mutex.Lock()
defer c.mutex.Unlock()


if err := c.Bill(); err != nil {
return err
}


if err := c.Notify(); err != nil {
return err
}


// ... do more stuff ...


return nil
}

此示例中的 defer确保无论 BillCustomer如何返回,mutex都将在 BillCustomer返回之前立即成为 unlocked。这是非常有用的,因为没有 defer,你必须记住 unlockmutex在函数可能的 return的每个地方。

参考文件

摘要:

当我们执行某些需要清理的操作时,我们可以“调度”清理操作,这些操作将在函数返回时运行,无论发生哪个路径,包括由于恐慌引起的路径。

详细回答:

  1. 编程语言努力提供一些结构,以便于更简单、更少出错的开发。(例如,当我们可以自己释放内存时,为什么 Golang 要支持垃圾收集)
  2. 一个函数可以在多个点返回。用户可能会忽略在某些路径中执行某些清理操作
  3. 某些清理操作并不与所有返回路径相关
  4. 另外,最好将清理代码保持在与需要清理的原始操作更接近的位置
  5. 当我们执行某些需要清理的操作时,我们可以“调度”清理操作,无论发生哪个路径,这些操作都会在函数返回时运行。