恢复#

延迟函数调用

如果你有一个无论如何希望确保运行的函数调用,都可以使用defer语句。你可以将defer关键字放在任何普通函数或方法调用之前,Go将延迟(也就是推迟)执行函数调用,直到当前函数退出之后。

defer语句是Go中一个非常有用的特性,可以将一个方法延迟到包裹该方法的方法返回时执行,在实际应用中,defer语句可以充当其他语言中try…catch…的角色,也可以用来处理关闭文件句柄等收尾操作。

package main

type name interface {
}
func main() {
	defer func() {
		println("close main")
	}()
	f := func(i1 int) {
		println(i1)
	}
	{
		defer func() {
			println("close close")
		}()
		f(11)
	}
	f(10)
}

image-20201105104416553

  • “defer”关键字确保函数调用发生,即使调用函数提前退出了。

列出目录中的文件

io/ioutil包包含一个ReadDir函数,它允许我们读取目录内容。向ReadDir传递一个目录的名称,它将返回一个值切片,每个值切片对应目录包含的每个文件或子目录(以及遇到的任何错误)。

每个切片的值都满足FileInfo接口,该接口包括一个返回文件名的Name方法和一个如果是目录则返回true的IsDir方法。

递归函数调用

package main

import (
   "errors"
   "io/ioutil"
   "path/filepath"
)

func readDir(path string,i int) error {
   dir, err := ioutil.ReadDir(path)
   if err!=nil {
      for ti:=0;ti<i;ti++ {
         print("    ")
      }
      println(path)
   }else {
      for _,info := range dir {
         join := filepath.Join(path, info.Name())
         if info.IsDir() {
            for ti:=0;ti<i;ti++ {
               print("    ")
            }
            println(path,"{")
            err := readDir(join, i+1)
            if err!=nil {
               return errors.New(join+" err")
            }
            for ti:=0;ti<i;ti++ {
               print("    ")
            }
            println("}")
         }else {
            for ti:=0;ti<i;ti++ {
               print("    ")
            }
            println(join)
         }
      }
   }
   return nil
}
func main() {
   err := readDir("./", 1)
   if err != nil {
      print("ERR")
   }
}

image-20201105113019323


panic

当程序出现panic时,当前函数停止运行,程序打印日志消息并崩溃。

可以通过简单地调用内置的panic函数来引发panic。

package main

func main() {
	panic("err")
	println("hello")
}

image-20201105113240690

  • panic函数需要一个满足空接口的参数(也就是说,它可以是任何类型)。该参数将被转换为字符串(如果需要),并作为panic日志信息的一部分打印出来。

堆栈跟踪

每个被调用的函数都需要返回到调用它的函数。为了实现这一点,就像其他编程语言一样,Go保持一个调用堆栈,即在任何给定点上处于活动状态的函数调用的列表。当程序发生panic时,panic输出中包含堆栈跟踪,即调用堆栈列表。这对于确定导致程序崩溃的原因很有用。

package main

func f1()  {
	println("1")
	f2()
}
func f2()  {
	println("2")
	f3()
}
func f3()  {
	println("3")
	f4()
}
func f4()  {
	println("4")
	f5()
}
func f5()  {
	panic("ERR")
	println("5")
}
func main() {
	f1()
}

image-20201105113554807


延迟调用在崩溃前完成

当程序出现panic时,所有延迟的函数调用仍然会被执行。如果有多个延迟调用,它们的执行顺序将与被延迟的顺序相反。

package main

func f1()  {
	println("1")
	f2()
}
func f2()  {
	defer println("defer 2")
	println("2")
	f3()
}
func f3()  {
	defer println("defer 3")
	println("3")
	f4()
}
func f4()  {
	defer println("defer 4")
	println("4")
	f5()
}
func f5()  {
	defer println("defer 5")
	panic("ERR")
	println("5")
}
func main() {
	f1()
}

image-20201105113836444

  • panic 在 defer之后打印。

“recover”函数

Go提供了一个内置的recover函数,可以阻止程序陷入panic。我们需要使用它来体面地退出程序。

在正常程序执行过程中调用recover时,它只返回nil,而不执行其他操作

func main() {
	println(recover()==nil)
}

image-20201105134311855

如果在程序处于panic状态时调用recover,它将停止panic。但是当你在函数中调用panic时,该函数将停止执行。因此,在panic所在的同一函数中调用recover没有意义,因为panic无论如何都会继续:

但是,当程序陷入panic时,有一种方法可以调用recover……在panic期间,任何延迟的函数调用都将完成。因此,可以在一个单独的函数中放置一个recover调用,并在引发panic的代码之前使用defer调用该函数。

调用recover不会导致在出现panic时恢复执行,至少不会完全恢复。产生panic的函数将立即返回,而该函数块中panic之后的任何代码都不会执行。但是,在产生panic的函数返回之后,正常的执行将恢复。

package main

func f1()  {
	println("1")
	f2()
}
func f2()  {
	defer println("defer 2")
	println("2")
	f3()
	println("Hello f2 end")
}
func f3()  {
	defer func() {
		if r := recover(); r != nil {
			println("recover err")
		}
	}()
	println("3")
	f4()
}
func f4()  {
	defer println("defer 4")
	println("4")
	f5()
}
func f5()  {
	defer println("defer 5")
	panic("ERR")
	println("5")
}
func main() {
	f1()
	println("Hello")
}

image-20201105135714065



本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

goroutine和channel 上一篇
接口 下一篇