游戏人生
About Me
  • 你好
  • Math
    • Number
      • Float IEEE754对确定性的影响
      • Pairing Function及其用途
    • Vector and Matrix
      • TRS基础概念
      • LossyScale深入分析
    • Quatenion
      • FromToRotation实现细节
    • Lerp and Curve
      • Slerp球形插值
      • Bezier Curve为什么重要
      • Interpolation和Extrapolation实现细节
  • Programming
    • C#
      • 学习资料
      • C# struct灵魂拷问
      • CIL的世界:call和callvirt
      • .NET装箱拆箱机制
      • .NET垃圾回收机制
    • Go
      • 基础特性
      • 如何正确的判空interface
      • 如何用interface模拟多态
      • 如何定制json序列化
      • 如何安全在循环中删除元素
      • 如何安全关闭channel
      • 如何集成c++库(cgo+swig)
      • 如何性能测试(benchmark, pprof)
    • Lua
      • 基础特性
  • General Game Development
    • Game Engine
      • 学习资料
      • 关于游戏引擎的认知
    • Networking
      • 帧同步
      • 状态同步
      • 物理同步
    • Physics
      • PhysX基本概念
      • PhysX增加Scale支持
      • PhysX场景查询
      • PhysX碰撞检测
      • PhysX刚体动力学
      • PhysX角色控制器
      • PhysX接入项目工程
      • 物理同步
      • 物理破坏
    • Design Pattern
      • 常用设计模式
      • MVP 架构模式
      • ECS 架构模式
  • Unity
    • Runtime
      • Unity拥抱CoreCLR
      • 浅析Mono内存管理
    • UGUI
      • 浅析UGUI渲染机制
      • 浅析UGUI文本优化
      • 介绍若干UGUI实用技巧
    • Resource Management
      • 浅析Unity堆内存的分类和管理方式
      • 深入Unity资源
      • 深入Unity序列化
      • 深入Assetbundle机制
    • Async
      • 深入Unity协程
      • 介绍若干Unity协程实用技巧
      • 异步动作队列
    • Hot Reload
      • Unity+Xlua
      • Xlua Examples学习(一)
      • Xlua Examples学习(二)
    • Editor Extension
    • Performance
      • 浅析Unity Profiler
      • 介绍一个Overdraw分析工具
  • Platform
    • WebGL
  • Real-world Project
    • Souce Engine
    • DOOM3 BFG
Powered by GitBook
On this page
  • Go
  • Why Use Go
  • Type
  • slice
  • map
  • struct
  • interface
  • channel
  • Reflection
  • Variable
  • Control flow
  • goroutine
  • concurrency
  • Tool
  • Compiler Tag
  1. Programming
  2. Go

基础特性

Go

Why Use Go

  • 编译快(语法简单)

  • 启动快,占用内存小(无运行时)

  • 部署快(docker/k8s支持好)

  • 容易编写高并发(goroutine等)、网络服务(官方网络通信库、序列化库、加解密等)

  • 内置工具丰富(CGO/Unit Test/Benchmark/pprof/template/...)

  • 强类型,但支持duck-typing这种动态语言的能力

Type

  • 值类型:built-in, array, struct

  • 引用类型: slice, map, channel, interface, function

  • 无enum, 通过 const+iota 模拟

  • 无class,只有值类型struct

    • 无继承,通过type embedding模拟

    • 无多态,通过将子类型真实对象传入父类型保存下来,显式调用真实对象的函数

  • 鸭子类型,但必须结合interface使用

  • 无法显式实现某个interface

    • 解决方案(如果没有实现会报错)。var _ IMyInterface = (*MyType)(nil)

  • interface{}

  • fmt.Sprintf(%T, a)

  • cat, ok := animal.(Cat) 这种用法非常常见,注意有时判断ok还不够

  • swtich t:= animal.(type)

  • reflect.TypeOf(a)

slice

  • 引用类型,可以为nil

  • 只占用3个指针大小(不含数据存储): 指向底层数组容器、长度、容量

  • 建议初始化时长度等于容量

  • 自动动态扩容(实际存储的array会发生变化,尤其注意)

  • slicing规则

    • s[:i]返回第i个元素以前(不含s[i])的切片。

    • s[i:]返回第i个元素及以后(含s[i])的切片。

var s []int // a nil slice
s := make([]SomeType, 0, 3) // empty slice with capacity 3
s := make([]SomeType, 3) // slice filled with 3 nils (do NOT use this)

s := []int{1, 2, 3} // len=cap=3
s = append(s, 4) // len=4, expanded cap=6

s2 := s[2:4] // len=2 cap=4
s2 = append(s2, 44, 55, 66)  // len=5, cap=8 (a new underlying array)
s2 = s2[:0] //len=0, cap=8. Just clear, no GC
s2 = nil // len=cap=0. Trigger GC

for idx, v := range s {
    ... // v is a copy
}

s4 := [][]int {{1}, {1, 2}} // high dimension

map

  • 基于HashMap,但性能一般。

m := map[string]int{"three": 3, "four": 4}
m["one"] = 1 // add or modify
delete(m, "three") // remove (but does NOT free any memory)
m = nil // free memory

v, ok := m["two"] // read key safely
if ok {
  // ...
}

for k, v := range m { // random order!
  // v is a copy
}

struct

  • 无法显式声明实现某个interface。

  • 无继承,通过组合实现封装、代码复用。为此还提供两个语法糖:anonymous field和promoted field。

  • 没有成员函数,而使用function with receiver 来模拟成员函数。

type Animal struct {
    name string
}
type Cat struct {
    Animal //anonymous field
    age int
}

// c as reference. More common.
func (c *Cat) Meow() {
    fmt.Println(c.Animal.name) 
    fmt.Println(c.name) // promoted field
}
// c as copy
func (c Cat) Meow() {
}

interface

  • go的interface本身也是一个对象,这个对象包含两个指针,分别指向实际对象、实际对象类型的方法表。这个设定的好处是实现动态语言般的duck typing,坏处是给判空带来额外的复杂性。

type iface struct {
  tab *itab
  data unsafe.Pointer
}
  • interface{}可以接收任意类型。

更多资料

  • https://halfrost.com/go_interface/

  • https://zhaolion.com/post/golang/upgrade/interface/

  • https://github.com/teh-cmc/go-internals/blob/master/chapter2_interfaces/README.md

channel

  • No-buffer channel: 收发端需要同时准备好,否则阻塞。

  • Buffered (Queue) channel. 当channel空时接收端阻塞,当channel满时发送端阻塞。

  • 只读型 <-chan, 只写型 chan<-

  • 可用于同步

  • 需要手动关闭close(c), 而不是通过发送nil

nobufferChannel := make(chan int) // `int` can be other types, including pointer
bufferedChannel := make(chan int, 10)

bufferedChannel <- 9 // send one int to channel

v, ok := <- bufferedChannel // receive one int from channel
if !ok {
  // channel is closed
}

for msg := range bufferedChannel {
  // auto break when channel close. "range" here is a sugar
}

// only sender should close. Sending to a closed channel will panic
close(bufferedChannel)

for { // inf loop
  select { // select randomly when any channel has content
  case msg1 := <- nobufferChannel:
    // do something
  case msg2 := <- bufferChannel:
    // do something
  }
}

// worker pool pattern
func worker(jobs <-chan int, results chan<- int) {
  for n:= range jobs {
    results <- n*n
  }
}
func main(){
  jobs := make(chan int, 10)
  results := make(chan int, 10)
  go worker(jobs, results)
  go worker(jobs, results)
  go worker(jobs, results)
  for i:= 0; i < 10; i++ {
    jobs <- i
  }
  close(jobs)
  for j:=0; j < 10; j++ {
    fmt.Println(<-results)
  }
}

Reflection

  • https://halfrost.com/go_reflection/

Variable

  • 大小学决定package外的可见性

  • 自带gc,使用静态逃逸分析决定变量在堆还是在栈上,无法人工控制。

  • new(T)只分配内存并清零。返回一个*T对象(指针)。简化写法&T{}。

  • make只用于slice, map, channel

s := make([]int, 100) // Allocate memory of 100 ints, initialize to 0
m := make(map[string]int) // Allocate memory for an empty <string, int> map
c := make(chan int)
m2 := map[string]int {} // Same effect as m. Called composite literals

Control flow

  • switch-case-fallthrough

  • select-case-channel

  • panic-recover

  • defer 离开当前scope时执行,多个defer按照LIFO(栈)顺序执行

goroutine

  • goroutine类似协程,是逻辑概念,由go调度。

  • goroutine和线程没有从属关系,不同goroutine可能分布在不同线程里。

    • 需要注意多线程race-condition:通过go build -race检测。

    • 必要时使用atomic, sync.Mutex等操作共享数据。

    • 常常使用channel起到同步和共享数据的作用。

  • 新建一个goroutine很简单,在普通函数调用前加go即可。

concurrency

专门讨论基于goroutine的并发和同步。

  • 简单情况下,使用channel即可同步。

  • 使用sync.WaitGroup。

  • 使用Context管理复杂情况。多层级或一组关联的goroutine(尤见于web server)。 https://pkg.go.dev/context

// Use channel to sync
func SyncByChannel() {
  num := 3
  channels := make([]chan int, num)
  for i:= 0; i<num; i++ {
    channels[i] = make(chan int)
    go DoJob(channels[i])
  }
  for i, ch := channels {
    <-ch
    // wait until goroutine i finishes
  }
  // all should finishes
}

func DoJob(ch chan int) {
  // when finish working, send a int as a signal
  ch <- 1
}

//Use WaitGroup to sync
func SyncByWaitGroup() {
  num := 3
  var wg sync.WaitGroup
  wg.Add(num)

  for i:= 0; i<num; i++ {
    go DoJob2(&wg)
  }

  wg.Wait()
  // all finishes
}

func DoJob2(wg *sync.WaitGroup) {
  // working
  wg.Done() // counter minus 1
}

Tool

  • go build

  • go test

  • go run race MyTest.go 检查goroutine是否有竞态

  • go run -gcflags "-m -l" .MyTest.go 检查是否有逃逸

  • go tool pprof

  • go generate

  • text template

Compiler Tag

  • go没有宏,但可以通过+build [tag1, tag2, ...]条件编译。支持多个tag(&&)。支持内置tag如linux, arm64,支持自定义tag(通过go build -tags ...指定)

  • //go:inline建议内联,//go:noinline强制不内联

  • go build -ldflags "-s -w"可以让编译出的包更小(因为剥离了符号表),也更加安全。

PreviousGoNext如何正确的判空interface

Last updated 20 days ago

非线程安全。

for range 遍历顺序随机(常见于CPU负载高时)。因此业务逻辑一定不能依赖遍历顺序。。

运行时判断Type的几种方法
How to check variable type at runtime in Go language
X does not implement Y (... method has a pointer receiver)
如何并发读写,stackoverflow
官方解释有意设计成随机