- go安装文件以及源代码
Downloads - The Go Programming Language
- 下载对应平台的二进制文件以及安装
- 环境变量
GOROOT: go的安装目录
GOPATH: src 存放源代码
pkg 存放依赖包
bin 存放可执行文件
其他常用变量: GOOS GOARCH GOPROXY
GOPROXY建议使用: export GOPROXY=
下载并安装 visual studio code
Visual Studio Code - Code Editing. Redefined
安装go语言插件
Go - Visual Studio Marketplace
- build: 编译包和依赖
- clean: 移除对象文件
- doc: 显示包或者符号的文档
- env: 打印go的环境信息
- bug: 启动错误报告
- fix: 运行go tool fix
- fmt: 运行gofmt进行格式化
- generate: 从processing source生成go文件
- get: 下载并安装包和依赖
- install: 编译并安装包和依赖
- list: 列出包
- run: 编译并运行go程序
- test: 运行测试
- tool: 运行go提供的工具
- version: 显示go的版本
- vet: 运行go tool vet
- mod: module maintenance
if condition1 {
// do something
} else if condition2 {
// do something else
} else {
// catch-all or default
}
if v:= x; v < 0 {
// do something
}
switch var1 {
case val1:
case var2:
fallthrough
case val3:
f()
default:
...
}
for i := 0; i < 100; i++ {
sum += 1
}
for ; sum < 100; {
sum += 10
}
for {
if condition {
break
}
// do something
}
const identifier type
var identifier type
myArray := [3] int{1, 2, 3} myslice := []int{1, 2, 3} 或者 myslice := myArray[:]// 切片添加 myslice = append(myslice[:], 1) // 切片删除 myslice = append(myslice[:index], myslice[index + 1:]...)
var c, java, python bool c = false java = true python = truevar i, j int = 1, 2 c, python := 3, false
myMap := make(map[string]string, 10) myMap["a"] = "b"myDict := map[int]int {2: 3, 3: 4} myFuncDict := map[string]func() int {"look": func() int {return 1}, }// 可以使用下面方式判断key在不在 value, exists := myMap["a"] if exists: {fmt.Println(value) }
type Human struct {first, last string }func (h *Human) getName() string {return h.first + h.last + " " }type IF interface {getName() string }interfaces := []IF{} h := new(Human) h.first = "ri" h.last = "bbon" interfaces = append(interfaces, h) for _, it := range interfaces {fmt.Name()) }// 结构体标签 type Mystruct struct {Name string `json:"name"` } mt := Mystruct{Name: "aa"} myType := reflect.TypeOf(mt) name := myType.Field(0) tag := name.Tag.Get("json") fmt.Println(tag)
var i int = 40
var f float64 = float64(i) 或者 f := float64(i)
New是返回指针的地址
Make则为返回第一个元素,可预设内存空间,避免未来的内存拷贝
// method 1 fmt.Println("os args is:", os.Args)//method 2 name := flag.String("name", "ribbon", "input you english name") flag.Parse()
Init函数会在包初始化的时候运行
当多个依赖引用统一的项目, 且被引用项目的初始化在init中完成,并且不可重复运行时,会导致启动错误
var myVariable int = 0func init() {myVariable = 1 }
func append(slice []Type, elems ...Type) []Type
func Callback(x int, f func(int, int)) {f(x, x)
}
方法: 作用在接受者上的函数
func (recv receiver_type) method(parameter_list) (return_value_list)
package mainimport "fmt"type Human struct {first, last string }func (h *Human) getName() string {return h.first + " " + h.last }type IF interface {getName() string }type Car struct {first1, last1 string }func (c Car) getName() string {return c.first1 + " " + c.last1 }func main() {interfaces := []IF{}h := new(Human)h.first = "ri"h.last = "bbon"c := new(Car)c.first1 = "car"c.last1 = "car1"interfaces = append(interfaces, h)interfaces = append(interfaces, c)for _, it := range interfaces {fmt.Name())}g := Human{}g.first = "aa"g.last = "cc"fmt.Name())c = new(Car)c.first1 = "ri"c.last1 = "bbon"fmt.Name()) }
TypeOf 返回被检查对象的类型
ValueOf返回被检查对象的值
package mainimport ("fmt""reflect" )type T struct {A stringb string }func (t T) GetString() string {return t.A + "-----" }func main() {my_map := make(map[string] string, 10)my_map["a"] = "b"t := reflect.TypeOf(my_map)v := reflect.ValueOf(my_map)fmt.Println(t, v, v.NumMethod())my_struct := T{A: "11", b: "cc"}fmt.Println(reflect.TypeOf(my_struct))v = reflect.ValueOf(my_struct)fmt.Println(v.NumMethod(), v.NumField(), v.Method(0).Call(nil), v.Field(0), v.Field(1)) }
- 可见性控制
- public - 常量、变量、类型、接口、结构、函数等的名称大写
- private - 非大写的只能包內使用
- 继承
- 通过组合实现, 内嵌一个或者多个struct
- 多态
- 通过接口实现,通过接口定义方法集, 编写多套实现
Marshal: 从struct --> string
Unmarshal: 从string --> struct
package mainimport ("encoding/json""fmt""reflect" )type Human struct {Name stringAge int }func StringToStruct(humanStr string) Human {h := Human{}err := json.Unmarshal([]byte(humanStr), &h)if err != nil {fmt.Println(err)}return h }func structToString(h Human) string {h.Age = 30000h.Name = "ribbon"updateBytes, err := json.Marshal(h)if err != nil {fmt.Println(err)}return string(updateBytes) }func main() {h := Human{}s := structToString(h)fmt.Println(s)human := StringToStruct(s)fmt.Println(reflect.ValueOf(human)) }
Json包使用map[string]interface{} 和 []interface{} 类型保存任意对象
package mainimport ("encoding/json""fmt" )type Human struct {Name stringAge int }func structToString(h Human) string {h.Age = 30000h.Name = "ribbon"updateBytes, err := json.Marshal(h)if err != nil {fmt.Println(err)}return string(updateBytes) }func main() {h := Human{}s := structToString(h)fmt.Println(s)var obj interface{}err := json.Unmarshal([]byte(s), &obj)if err != nil {return}objMap, _ := obj.(map[string]interface{})for key, value := range objMap {switch value.(type) {case string:fmt.Printf("%s: %s ", key, value)case int:fmt.Printf("%s: %d ", key, value)default:fmt.Printf("%s: %v ", key, value)}}var obj1 map[string] interface{}err = json.Unmarshal([]byte(s), &obj1)if err != nil {return}for key, value := range obj1 {switch value.(type) {case string:fmt.Printf("%s: %s ", key, value)case int:fmt.Printf("%s: %d ", key, value)default:fmt.Printf("%s: %v ", key, value)}} }
Go没有内置的exception机制, 只提供error接口供定义错误
type error interface {
Error() string
}
可以通过error.New or fmt.Errorf创建新的error
通常应用程序对error的处理大部分是判断error是否为nil
defer这个相当于python java的finally
panic:可以再系统出现不可恢复错误的时候主动调用panic, panic会使当前线程直接crash
recover: 函数从panic或者错误场景中恢复
defer func() {if err := recover(); err != nil {fmt.Println(err)} } () panic("njson is errorn")
协程属于轻量级线程
Go语言在runtime、系统调用等多方面对goroutine调度进行了封装和处理,当遇到长时间执行或者进行系统调用时,会主动把当前goroutine的CPU(P)转让出去, 让其他goroutine能被调度,也就是golang从语言层面支持了协程
每个goroutine默认占用内存远比线程的小(goroutine:2KB 线程:8MB)
goroutine切换开销方面远比线程小(线程涉及切换【从用户态切换到内核态】、16个寄存器、PC/SP等寄存器刷新, goroutine只涉及PC/SP/DX三个寄存器值得修改)
package mainimport ("fmt""time" )func main() {for i := 0; i < 10; i++ {go fmt.Println(i)}time.Sleep(time.Second) }
描述两个独立的并发实体通过共享的通讯channel进行通信的并发模型
协程之间虽然解耦,但是它们和Channel有着耦合
package mainimport ("fmt" )func main() {ch := make(chan int)go func() {ch <- 0} ()fmt.Println(<-ch) }
waitGroup
package mainimport ("fmt""sync" )type worker struct {in chan intdone func() }func dowork(id int, work worker) {for i := range work.in{work.done()fmt.Printf("%d %cn", id, i)} }func createWorker(id int, wg *sync.WaitGroup) worker{w := worker{in: make(chan int),done: func() {defer wg.Done()},}go dowork(id, w)return w }func chanDemo() {var wg sync.WaitGroupvar worker[10] workerfor i:=0; i<10; i++{worker[i] = createWorker(i, &wg)}wg.Add(30)for i, work := range worker{work.in <- 'T'+ i}for i:=0; i<10; i++{worker[i].in <- 'B' + i}for i:=0; i<10; i++{worker[i].in <- 'a' + i}wg.Wait() }func main() {chanDemo() }
select
package mainimport ("fmt""time" )func main() {ch := make(chan int)go func() {ch <- 0} ()fmt.Println(<-ch)ch2 := make(chan int, 10)ch3 := make(chan int, 10)tm := time.After(10 * time.Second)go func() {for i := 0; i < 10; i++ {ch2 <- i}close(ch2)} ()go func() {for i := 10; i < 20; i++ {ch3 <- i}close(ch3)} ()for {select {case v := <-ch2:fmt.Println("ch2", v)case v := <-ch3:fmt.Println("ch3", v)case <- tm:return}} }
context
package mainimport ("context""fmt""time" )func main() {ctx, cancel := context.WithCancel(context.Background())go func(ctx context.Context) {for {select {case <-ctx.Done():fmt.Println("监控退出,停止了...")returndefault:fmt.Println("goroutine监控中...")time.Sleep(2 * time.Second)}}}(ctx)time.Sleep(10 * time.Second)fmt.Println("可以了,通知监控停止")cancel()//为了检测监控过是否停止,如果没有监控输出,就表示停止了time.Sleep(5 * time.Second) }
package mainfunc main() {unsafeWrite() }func unsafeWrite() {conflictMap := map[int]int{}for i := 0; i <= 100; i++ {go func() {conflictMap[1] = i}()} }
当多线程对同一个程序进行赋值操作的时候,会导致如下图的异常报错信息,需要添加锁进制在代码中。
- GO语言不仅仅提供基于CSP的通讯模型,也支持基于共享内存的多线程数据访问
- Sync 包提供了锁的基本原语
sync.Mutex互斥锁
package mainimport ("fmt""sync""time" )type SafeMap struct {safeMap map[int]intMutex sync.Mutex }func safeWrite() {conflictMap := SafeMap{safeMap: map[int]int{}, Mutex:sync.Mutex{}}for i := 0; i <= 100; i++ {go func() {conflictMap.Mutex.Lock()conflictMap.safeMap[1] = iconflictMap.Mutex.Unlock()}()}time.Sleep(time.Second)fmt.Println(conflictMap.safeMap[0], conflictMap.safeMap[1]) }func main() {safeWrite() }
sync.RWMutex 读写分离锁
package mainimport ("fmt""sync""time" )type SafeMap struct {safeMap map[int]intMutex *sync.RWMutex }func safeWrite(conflictMap SafeMap, i int) {conflictMap.Mutex.Lock()conflictMap.safeMap[1] = iconflictMap.Mutex.Unlock() }func safeRead(conflictMap SafeMap) {conflictMap.Mutex.RLock()fmt.Println(conflictMap.safeMap[1])conflictMap.Mutex.RUnlock() }func main() {conflictMap := SafeMap{safeMap: map[int]int{}, Mutex: &sync.RWMutex{}}for i := 0; i <= 100; i++ {go safeWrite(conflictMap, i)go safeRead(conflictMap)}time.Sleep(time.Second) }
sync.WaitGroup 等待一组group返回
package mainimport ("fmt""sync" )func main() {waitGroup := sync.WaitGroup{}waitGroup.Add(100)for i := 0; i <= 100; i++ {go func(i int) {fmt.Println(i)waitGroup.Done()} (i)}waitGroup.Wait() }
sync.Once 保证某段代码只执行一次
package mainimport ("fmt""sync" )func main() {var once sync.OnceonceBody := func() {fmt.Println("Only once")}done := make(chan bool)for i := 0; i < 10; i++ {go func() {once.Do(onceBody)done <- true}()}for i := 0; i < 10; i++ {<-done} }
sync.Cond 让一组goroutine在满足特定条件的时候被唤醒, k8s中的队列, 标准的生产者消费者模式
package mainimport ("fmt""sync""time" )type Queue struct {queue []stringcond *sync.Cond }func (q *Queue) Enqueue (item string) {q.cond.L.Lock()d.L.Unlock()q.queue = append(q.queue, d.Broadcast() }func (q *Queue) Dequeue () d.L.Lock()d.L.Unlock()if len(q.queue) == 0 {fmt.Println("no data avaliable, wait")q.cond.Wait()}result := q.queue[0]q.queue = q.queue[1:]return result }func main() {q := Queue{queue: []string{}, cond: sync.NewCond(&sync.Mutex{})}go func() {for {q.Enqueue("a")time.Sleep(time.Second * 2)}} ()go func() {for {result := q.Dequeue()fmt.Println(result)time.Sleep(time.Second * 2)}} ()time.Sleep(time.Second * 10) }
进程: 资源分配的基本单位
线程: 调度的基本单位
无论是线程还是进程,在linux中以task_strruct描述, 从内核角度看,与进程无本质区别。Glibc中的pthread库提供NPTL(Native POSIX Threading Library)支持
进程中有自己的mm(内存模型)、 fs(文件系统)、file(文件)、signal(信号量),独立维护, 而线程则共用同一套mm(内存模型)、fs(文件系统)、 file(文件) 、signal(信号量)。所谓线程即是没有独立资源的进程。
Linux内存调用
通过objdump 来看编译的二进制文件, 在这里定义了很多text .bss .data, text指指令有多大,data表示有多少初始化数据的值, bss表示有多少未初始化的值,dec指前面三个值得总和。
物理内存按照4096字节来进行分页,物理内存和虚拟内存是通过多级页表(常见的为四级页表)来进行对应,通过索引找到对应的page table再加上offset找到真正的物理地址。
CPU对内存的访问
CPU把虚拟地址传给MMU(内存管理单元), MMU去物理内存查询页表,得到实际的物理地址。CPU会维护TLB(缓存虚拟地址和物理地址的映射关系)
进程切换开销
- 直接开销
- 切换页表全局目录(PGD)
- 切换内核态堆栈
- 切换硬件上下文(进程恢复前,必须装入寄存器的数据统称硬件上下文)
- 刷新TLB
- 系统调度器的代码执行(TEXT)
- 间接开销
- CPU缓存失效导致进程需要到内存直接访问的IO操作变多
线程切换开销
线程相对于进程间节省了虚拟地址空间的转换。
用户线程
GO语言基于GMP模型实现用户态线程,Go语言GMP模型_bingshiwuyu的专栏-CSDN博客参考这篇文章可以很好的了解GMP模型。
两种常见的内存管理方法:堆和内存池_Melody1994的博客-CSDN博客_内存堆和内存池 可以参考这篇文章
TCMalloc机制文章:图解 TCMalloc - 知乎
Go语言内存分配: 图解Go语言内存分配 - 知乎
Go语言GC工作原理: Go 语言GC(垃圾回收)的工作原理 - 简书
指针的对象是在堆上面这个是需要GC回收的,局部变量对象(类似于struct)是在栈上当使用函数结束则生命周期也就结束
当前只要学习gomod 或者 使用原生的GOROOT GOPATH来进行维护就可以,个人推荐使用go mod的方式
1. go mod使用方式
- 切换mod开启模式: export GO111MODULE=on/off/auto. 在/etc/profile文件上添加
- 使用GOPROVATE来指定私有代码仓库
- 创建项目在GOPATH/src目录下
- 初始化Go模块
- 下载依赖包添加缺少的依赖包瘦身, 会下载到GOPATH/pkg/mod目录下
- Go依赖添加到vendor目录下
- 可以看go.mod文件看使用GO版本和下载的包的版本, 如果要下载其他版本在init后先修改对应的版本号
可以使用replace来指定代码仓库或者指定版本
本文发布于:2024-02-01 05:05:58,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170673515834087.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |