Golang整理

1. go struct能不能比较

  • 如果结构体的所有成员变量都是可比较的,那么结构体就可比较
  • 如果结构体中存在不可比较的成员变量,那么结构体就不能比较
  • 结构体之间能进行强制转换也可以比较。结构体之间进行转换需要他们具备完全相同的成员(字段名、字段类型、字段个数)
  • 指针比较实际上是比较指针指向的内存地址,而不是指针变量的内存地址
package main
import "fmt"
type Student struct {Name stringAge  intSlice []intMap map[string]int
}
func main() {s1 := Student{Name:  "s1",Age:   20,Slice: []int{1,2,3},Map: map[string]int{"1": 1, "2": 2},}s2 := Student{Name:  "s1",Age:   20,Slice: []int{1,2,3},Map: map[string]int{"1": 1, "2": 2},}fmt.Println(s1 != s2) // 不能比较,因为结构体包含不可比较类型
}
package main
import "fmt"
type Student struct {Name stringAge  int
}
func main() {s1 := Student{Name: "s1",Age:  20,}s2 := Student{Name: "s1",Age:  20,}fmt.Println(s1 == s2) // true
}
package main
import "fmt"
type Student struct {Name stringAge  int
}
type Driver struct {Name stringAge  int
}
func main() {s1 := Student{Name: "s1",Age:  20,}d1 := Driver{Name: "s1",Age:  20,}// fmt.Println(s1 == d1) // 报错fmt.Println(s1 == Student(d1)) // 可以比较
}

2. go defer

  • 延迟函数的参数在defer语句出现时就已经确定下来了
  • 延迟函数可能操作主函数的具名返回值
  • 延迟函数执行按后进先出顺序执行,即先出现的defer最后执行
package main
import "fmt"
func main(){a := 1b := 2defer calc(a, calc(a,b))a = 0defer calc(a, calc(a,b))
}
func calc(x,y int) int {fmt.Println(x,y,x+y)return x+y
}
package main
import "fmt"
func main()  {defer func() {fmt.Println(1)}()defer func() {fmt.Println(2)}()defer func() {fmt.Println(3)}()panic(1) // 程序发生panic 会先处理defer然后panic
}
package main
import "fmt"
func printArray(array *[3]int) {for i := range array {fmt.Println(array[i])}
}
func deferFuncParameter() {var aArray = [3]int{1, 2, 3}defer printArray(&aArray)aArray[0] = 10return
}
func main() {deferFuncParameter() // 10 2 3
}

3. go select

  • select就是用来监听和channel有关的IO操作,当 IO 操作发生时,触发相应的动作
  • 如果有一个或多个IO操作可以完成,则Go运行时系统会随机的选择一个执行(底层是打乱顺序,然后遍历每个channel,遇到第一个就return)
  • 执行default分支语句,如果连default都没有,则select语句会一直阻塞,直到至少有一个IO操作可以进行
  • 每个case都会被计算一遍
  • 可以使用break结束一次case的执行
package main
import "fmt"
var ch1 chan int
var ch2 chan int
var chs = []chan int{ch1, ch2}
var numbers = []int{1, 2, 3, 4, 5}
func main () {select {case getChan(0) <- getNumber(2):fmt.Println("1th case is selected.")case getChan(1) <- getNumber(3):fmt.Println("2th case is selected.")default:fmt.Println("default!.")}
}
func getNumber(i int) int {fmt.Printf("numbers[%d]\n", i)return numbers[i]
}
func getChan(i int) chan int {fmt.Printf("chs[%d]\n", i)return chs[i]
}
select走的是default,但是每个case都会被计算一次
chs[0]
numbers[2]
chs[1]
numbers[3]
default!.
package main
import "fmt"
func main()  {ch1 := make(chan int, 1)ch2 := make(chan int, 1)ch1 <- 3ch2 <- 5select {case <- ch1:fmt.Println("ch1 selected.")breakfmt.Println("ch1 selected after break")case <- ch2:fmt.Println("ch2 selected.")fmt.Println("ch2 selected without break")}
}

4. go context 用途

  • 上下文,追踪goroutine,控制goroutine停止,包含 goroutine 的运行状态、环境、现场等信息
  • context.WithDeadline()
  • context.WithValue()
  • context.WithTimeout()
  • context.WithCancel()

5. 主协程等待其余协程执行完后退出

  • 使用channel
package mainimport "fmt"var ch = make(chan int)
func person1() {fmt.Println("person1")ch <- 1
}
func person2() {fmt.Println("person2")ch <- 2
}
func main() {go person1()go person2()count := 2// 判断所有协程是否退出for range ch {count--if 0 == count {close(ch)}}fmt.Println("main goroutine finished")
}
  • 使用sync.WaitGroup
package main
import ("fmt""runtime""sync"
)
var wg = sync.WaitGroup{}
func main()  {fmt.Println("main goroutine start...")wg.Add(2)runtime.GOMAXPROCS(1)for i := 0; i < 2; i++{go func(i int) {fmt.Println(i)wg.Done()}(i)}wg.Wait()fmt.Println("main goroutine finished...")
}
  • 使用select
package main
import ("fmt""time"
)
func main()  {fmt.Println("main goroutine start...")ch := make(chan int)go func() {fmt.Println("sub goroutine running")time.Sleep(time.Second * 2)ch <- 1fmt.Println("sub goroutine finished")}()select {case <- ch:fmt.Println("main goroutine finished...")}
}
  • 使用context
package main
import ("context""fmt""time"
)
func main()  {fmt.Println("main goroutine start...")ctx, cancel := context.WithTimeout(context.Background(), time.Second * 3)go func() {for{select {case <-ctx.Done():fmt.Println("select ctx done")default:time.Sleep(time.Millisecond * 100)fmt.Println("select default")}}}()time.Sleep(time.Second * 1)cancel()fmt.Println("main goroutine finished...")
}

6. map顺序读取

  • map本无法顺序读取
  • 先遍历所有的key,然后对key排序后,按顺序取出value
package main
import ("fmt""sort"
)
func main() {hash := map[string]int{"jerry":   10,"tom":     2,"panghu":  39,"xiaofu":  30,"daxiong": 40,}keys, i := make([]string, len(hash)), 0for key, _ := range hash {keys[i] = keyi++}sort.Strings(keys)for _, key := range keys {fmt.Println(key, hash[key])}
}

7. slice,len,cap,共享,扩容

  • slice 底层结构
    • 指向数组的指针
    • 长度
    • 容量
  • slice扩容
    • 长度小于1024 每次容量翻倍
    • 大于1024 每次 涨25%
package main
import "fmt"
func main() {a := make([]int, 2, 2)a[0], a[1] = 1, 2b := append(a[0:1], 3)c := append(a[1:2], 4)fmt.Println(b, c) // [1,3],[3, 4] c赋值时底层数组已经发生变化了
}
package main
import "fmt"
func main() {a := make([]int, 2, 2)a[0], a[1] = 1, 2b := make([]int, 1)copy(b, a[0: 1])b = append(b, 3)c := make([]int, 1)copy(c, a[1:2])c = append(c, 4)fmt.Println(b, c) // [1,3], [2,4]
}

8. go copy函数

  • 目标数组长度不足拷贝长度时,只拷贝目标数组长度
  • 两个数组切片不一样大,就会按其中较小的那个数组切片的元素个数进行复制

9. go 实现集合

package mainimport ("fmt""sync"
)type Set struct {set map[string]struct{}lock sync.Mutex
}func (s *Set) AddElem(elem string) {s.lock.Lock()defer s.lock.Unlock()s.set[elem] = struct{}{}
}func (s *Set) RemoveElem(elem string) {s.lock.Lock()defer s.lock.Unlock()delete(s.set, elem)
}func (s *Set) Smembers() []string {members, i := make([]string, len(s.set)), 0for key, _ := range s.set {members[i] = keyi++}return members
}func (s *Set) Size() int {return len(s.set)
}func main() {s := Set{set: map[string]struct{}{}}s.AddElem("111")s.AddElem("222")fmt.Println(s.Smembers())s.RemoveElem("222")fmt.Println(s.Smembers())
}

10. golang 实现消息队列

  • 通过切片简单实现
package main
import ("errors""fmt""sync""time"
)
type MsgQueue struct {queue []intlock  sync.Mutex
}
func (q *MsgQueue) AddMsg(i int) {q.lock.Lock()defer q.lock.Unlock()q.queue = append(q.queue, i)
}
func (q *MsgQueue) GetMsg() (int, error) {q.lock.Lock()defer q.lock.Unlock()if len(q.queue) == 0 {return 0, errors.New("queue blank")}msg := q.queue[0]q.queue = q.queue[1:]return msg, nil
}
func main() {q := MsgQueue{queue: []int{}}for i := 0; i < 100; i++ {go func(i int) {q.AddMsg(i)}(i)}for {fmt.Println(q.GetMsg())time.Sleep(100 * time.Millisecond)}
}

11. Go大文件排序

  • 思路
    • 大文件分隔成小文件,然后排序写入小文件
    • 打开所有排好序的小文件,每次取出最小的一个