Go异常处理

栏目: Go · 发布时间: 5年前

内容简介:error的声明创建一个errorerror的使用
type error interface { 
    Error() string
}

error的声明

创建一个error

if path == "" {
    return nil, errors.New("The parameter is invalid!")
}

error的使用

func readFile(path string) ([]byte, error) {
    file, err := os.Open(path)
    if err != nil {
        return nil, err
    }
    defer file.Close()
    return ioutil.ReadAll(file)
}

异常处理——panic

译为运行时恐慌

内建函数 panicrecover 是天生的一对。前者用于产生运行时恐慌,而后者用于“恢复”它。

不过要注意, recover 函数必须要在 defer 语句中调用才有效。因为一旦有运行时恐慌发生,当前函数以及在调用栈上的所有代码都是失去对流程的控制权。只有 defer 语句携带的函数中的代码才可能在运行时恐慌迅速向调用栈上层蔓延时“拦截到”它。

defer func() {
    if p := recover(); p != nil {
        fmt.Printf("Fatal error: %s\n", p)
    }
}()

panic 函数。该函数可接受一个 interface{} 类型的值作为其参数。也就是说,我们可以在调用 panic 函数的时候可以传入任何类型的值。不过,我建议大家在这里只传入 error 类型的值。这样它表达的语义才是精确的。更重要的是,当我们调用 recover 函数来“恢复”由于调用 panic 函数而引发的运行时恐慌的时候,得到的值正是调用后者时传给它的那个参数。因此,有这样一个约定是很有必要的。

用法

package main
import (
    "errors"
    "fmt"
)

func innerFunc() {
    fmt.Println("Enter innerFunc")
    panic(errors.New("Occur a panic!"))
    fmt.Println("Quit innerFunc")
}

func outerFunc() {
    fmt.Println("Enter outerFunc")
    defer func() {
        if p := recover(); p != nil {
            fmt.Printf("Fatal error: %s\n", p)
        }
    }()
    innerFunc()
    fmt.Println("Quit outerFunc")
}

func main() {
    fmt.Println("Enter main")
    outerFunc()
    fmt.Println("Quit main")
}

go语句

go 语句的执行会很快结束,并不会对当前流程的进行造成阻塞或明显的延迟。

go 语句被执行时,其携带的函数(也被称为 go 函数)以及要传给它的若干参数(如果有的话)会被封装成一个实体(即Goroutine),并被放入到相应的待运行队列中。Go语言的运行时系统会适时的从队列中取出待运行的Goroutine并执行相应的函数调用操作。注意,对传递给这里的函数的那些参数的求值会在 go 语句被执行时进行。这一点也是与 defer 语句类似的。

由于 go 函数的执行时间的不确定性,所以 Go 语言提供了很多方法来帮助我们协调它们的执行。其中最简单粗暴的方法就是调用 time.Sleep 函数。

package main

import (
    "fmt"
    "time"
)

func main() {
    go fmt.Println("Go!")
    time.Sleep(100 * time.Millisecond)
}

另一个比较绅士的做法是在 main 函数的最后调用 runtime.Gosched 函数。

package main

import (
    "fmt"
    "runtime"
)

func main() {
    go fmt.Println("Go!")
    runtime.Gosched()
}

runtime.Gosched 函数的作用是让当前正在运行的Goroutine(这里是运行 main 函数的那个Goroutine)暂时“休息”一下,而让Go运行时系统转去运行其它的Goroutine(这里是与 go fmt.Println("Go!") 对应并会封装 fmt.Println("Go!") 的那个Goroutine)。如此一来,我们就更加精细地控制了对几个Goroutine的运行的调度。

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    wg.Add(3)
    go func() {
        fmt.Println("Go!")
        wg.Done()
    }()
    go func() {
        fmt.Println("Go!")
        wg.Done()
    }()
    go func() {
        fmt.Println("Go!")
        wg.Done()
    }()
    wg.Wait()
}

sync.WaitGroup 类型有三个方法可用—— AddDoneWaitAdd 会使其所属值的一个内置整数得到相应增加, Done 会使那个整数减 1 ,而 Wait 方法会使当前Goroutine(这里是运行 main 函数的那个Goroutine)阻塞直到那个整数为 0 。这下你应该明白上面这个示例所采用的方法了。我们在 main 函数中启用了三个Goroutine来封装三个 go 函数。每个匿名函数的最后都调用了 wg.Done 方法,并以此表达当前的 go 函数会立即执行结束的情况。当这三个 go 函数都调用过 wg.Done 函数之后,处于 main 函数最后的那条 wg.Wait() 语句的阻塞作用将会消失, main 函数的执行将立即结束。

func main() {

    ch1 := make(chan int, 1)
    ch2 := make(chan int, 1)
    ch3 := make(chan int, 1)
    go func() {
        fmt.Println("1")
        ch1 <- 1
    }()

    go func() {
        fmt.Println("2")
        ch2 <- 2
    }()

    go func() {
        fmt.Println("3")
        ch3 <- 3
    }()

    <-ch3
}
/*
最终 print :
1
2
3
*/

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

支付战争

支付战争

埃里克•杰克逊 / 徐彬、王晓、清华大学五道口金融学院未央研究 审译 / 中信出版社 / 2015-5-19 / 49.00

这是一个野心勃勃的创业计划,在线支付鼻祖PayPal试图创造一个“统治世界”的金融操作系统,并在全球成功推广一款颠覆式的互联网产品。 《支付战争》的作者是“PayPal黑帮”成员之一,他真实还原了这个伟大产品是如何诞生的,以及在后来的发展壮大之路上,如何应对融资紧张、突破增长瓶颈,在竞争者凶猛围剿与平台商霸王条款的夹击下,逆境求生,改变业务模式,最终完成IPO,并成功出售给竞争对手eBay的......一起来看看 《支付战争》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

MD5 加密
MD5 加密

MD5 加密工具