Golang整理

1. go struct能不能比较

  • 如果结构体的所有成员变量都是可比较的,那么结构体就可比较
  • 如果结构体中存在不可比较的成员变量,那么结构体就不能比较
  • 结构体之间能进行强制转换也可以比较。结构体之间进行转换需要他们具备完全相同的成员(字段名、字段类型、字段个数)
  • 指针比较实际上是比较指针指向的内存地址,而不是指针变量的内存地址
package main
import "fmt"
type Student struct {
	Name string
	Age  int
	Slice []int
	Map 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 string
	Age  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 string
	Age  int
}
type Driver struct {
	Name string
	Age  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 := 1
	b := 2
	defer calc(a, calc(a,b))
	a = 0
	defer 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] = 10
	return
}
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 <- 3
	ch2 <- 5
	select {
	case <- ch1:
		fmt.Println("ch1 selected.")
		break
		fmt.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 main

import "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 <- 1
		fmt.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)), 0
	for key, _ := range hash {
		keys[i] = key
		i++
	}
	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, 2
	b := 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, 2
	b := 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 main

import (
	"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)), 0
	for key, _ := range s.set {
		members[i] = key
		i++
	}
	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 []int
	lock  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大文件排序

  • 思路
    • 大文件分隔成小文件,然后排序写入小文件
    • 打开所有排好序的小文件,每次取出最小的一个
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值