VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > go语言 >
  • GoLang 学习笔记(六)--Effective Go(高效编程风格)(二)

1. 数据(书接上回)

1.1 map(映射)

slice 不能用作 key,因为并没有定义两个 slice 是否相等的手段。

1.2 String() 方法

如果要在 String() 方法(Stringer 接口)里使用 Sprintf,请不要使用使用 %s 或者 字符串的 %v,因为一这又会再次调用 String() 方法。然后就会无限递归。

1.3 append

go 自己的 append 你是没办法自己写出来的(不过你可以写一个不那么强大的,只能 append 单个类型的函数),append 的实现需要编译器的帮助。因为 append 接收的 slice 的类型是不确定的。而在 go 中,你是无法在执行过程中改变函数的形参类型的。

1.4 ...

善用 ...

2. 初始化

  1. 枚举常量可以用 iota
  2. init 函数:
    1. 每个 .go 文件都可以包含多个 init 函数,这些函数会在所有全局变量初始化结束后调用。这么设置的目的是为了表示 init 函数运行=初始化完毕。
    2. init 的作用有两个:
      1. 由于常量的值必须是常量,不能是调用函数生成的。init 函数里可以放置这些变量。
      2. 校验程序。

3. 接口

Effective Go 里又再次给接口加了一条很有用的说明,或者受定义。

如果一个类型能够实现这些方法,那么就能够用在这里。

“能够用在这里”:在某些时候表现为,就能够使用我们提供的函数。

3.1 常用的接口

比较常用的接口有:

  1. 世人皆知 Stringer
  2. sort.Interface:
    package sort
    type Interface interface {
        Len() int            // 获取元素数量
        Less(i, j int) bool // i,j 是 index
        Swap(i, j int)      // 交换
    }
    
    然后就可以用 sort.Sort(<你的类型>) 排序了。
    不过实际上 sort 提供了很多类型转换函数,可以让很多类型的数据如 []int 不需要手动实现该接口就可以排序了。

3.2 没用的小知识

实际上是先有类型选择,再有类型断言。类型断言借鉴了类型选择的语法。

3.3 通用性(这里解释了接口为什么要设计个可以储存值的设定)

如果一个类型只实现了某一个接口,且这个类型并没有实现任何接口以外的方法,那就没有必要导出这个类型。
如果是这种情况下,一个构造函数就应该返回这个接口类型的值,而不是那个未导出的类型。

比如在 hash 这个库里,有 crc32.NewIEEE 和 adler32.New两种构造函数,虽然看起来它们应该各自返回 crc32 和 adler32 相关的某个类型,但其实它们返回的是同一个 hash.Hash32 的接口类型的值(这也是为什么要设计成接口可以储存所有实现这个接口方法的类型)。这样想让你的代码从使用 crc32 转成使用 adler32 就非常简单了,无需改动其他的,只需要换个构造函数即可。

  • 换个理解方式,无论是海尔洗衣机还是格力洗衣机,都是洗衣机,无论调用海尔还是格力函数,返回的都是洗衣机这个接口类型的值(这个值里可能储存海尔,也可能储存格力,但这都无所谓)。这样后续的代码就不用管到底是海尔还是格力了

(不过说实话,这也是因为强类型语言才需要这种东西来松耦合,弱类型语言根本就不用接口就能实现这种程度的事情)

4. 空白标识符

除了广为人知的用法以外。还可以:

var a = 1 // 不想这么早用,但是编译器老是报错,烦死了
_ = a // 好了

5. 内嵌

  1. 接口内嵌:就相当于多个接口的方法
  2. 类型内嵌:注意和子类型的区别
    type Teacher struct {
        people *People // 正常子类型
    }
    
    type Teacher struct {
        *People // 内嵌类型,不写字段名
    }
    
    var teacher Teacher
    
    区别在于,使用内嵌类型后,可以直接通过 teacher.Method() 类型来调用 *People 的方法。但是如果是正常子类型,就要 teacher.people.Method() 来调用。好处不只是这样,想象一下接口。Teacher 直接实现了 *People 所满足的接口。但是如果是正常子类型的话,就要在 Teacher 上再次实现这个接口的方法(也叫接口转发),然后在这个方法里调用 *teacher.people.Method(),才能实现接口转发,这样就比较麻烦。
    • 不过内部其实还是有一个隐含的字段,调用 teacher.Method(),实际上还是调用内部 *People 类型的方法。
    • 而且仍旧可以通过和类型相同的字段来引用,比如上面的第二个结构体,其实还是可以通过 teacher.People 来引用的
      具体看这个页面。

6. 并发

并发的东西有些不好理解,请参阅这篇文章。

6.1 为什么要叫做 goroutine(go 程)

因为要和现有术语(线程,协程,进程)区分开来。

6.2 匿名函数

go 中常用匿名函数来生成 goroutine。

6.3 信道的各种用法

  1. 最基础的,通信数据
  2. 使用一个无缓冲信道,来控制同步,信道可以传一些没有意义的数据,但是通过无缓冲的特性,可以控制流程。image.png
  3. 使用缓冲信道来控制吞吐量,依旧是传输无用的数据达到控制流程的目的。下面的代码控制了同时最多只能有 MaxOutstanding 个 process 同时运行。image.png
  4. 可以根据 CPU 的数量,进行优化并发(可以通过 runtime.NumCPU() 获取 CPU 的数量)image.png
    不过,有些时候用户会自己分配 CPU,可能会限制我们程序最大能使用多少个 CPU,为了尊重用户的选择。出现了另一个函数 numCPU = runtime.GOMAXPROC(0)(传入参数 0 是为了返回值),如果用户没有设置,就返回 runtime.NumCPU

6.4 并发 和 并行 的区别

并发:用 可独立执行的组件 构建程序。
并行:为了提高效率,同时使用多个 CPU。

尽管运用 Go 的并发特性能够在很多时候达到并行的效果。但是 Go 本身只是为了并发。很多并行问题,GO 并不适合。

7. 错误

原文在此。

7.1 panic

一般来说,出错的时候都是返回一个 err(各种 _, ok = xxx)。但是如果这是个不可恢复的错误呢?我们就是想要在出错的时候终止程序呢?
panic 就是为此而生的,panic 会终止程序。

panic 接收一个任意类型的参数,一般是字符串,并会在程序终止的时候打印出来。

如果问题可以被解决,就尽量不要 panic,而是返回一个 error。除非真的之后完全进行不下去了。

7.2 recover

在调用 panic 的时候,程序会立刻终止,然后开始回溯 goroutine 栈,运行所有的 defer,直到到达栈顶端,最终完全终止。
不过我们可以调用 recover 来让程序变成正常,因为此时只有 defer 能正常运行,recover 只能放在 defer 里。

  • 一个重要的作用就是,当某个 goroutine 产生 panic 的时候,不要影响其他的 goroutine,代码如下:
    image.png
    当上面的 do(work) 中调用了 panic(),那么只会停止这个 safelyDo() 调用,不会影响其他的 goroutine。
  • 3
  • 出处:https://www.cnblogs.com/oyishyi/p/14818724.html

相关教程