go安装文件以及源代码二. IDE设置Downloads - The Go Programming Language
下载对应平台的二进制文件以及安装环境变量GOROOT: go的安装目录
GOPATH: src 存放源代码
pkg 存放依赖包
bin 存放可执行文件
其他常用变量: GOOS GOARCH GOPROXY
GOPROXY建议使用: export GOPROXY=https://goproxy.cn
三.GO语言一些基本命令下载并安装 visual studio code
Visual Studio Code - Code Editing. Redefined
安装go语言插件
Go - Visual Studio Marketplace
build: 编译包和依赖clean: 移除对象文件doc: 显示包或者符号的文档env: 打印go的环境信息bug: 启动错误报告fix: 运行go tool fixfmt: 运行gofmt进行格式化generate: 从processing source生成go文件get: 下载并安装包和依赖install: 编译并安装包和依赖list: 列出包run: 编译并运行go程序test: 运行测试tool: 运行go提供的工具version: 显示go的版本vet: 运行go tool vetmod: module maintenance四. Go语言的基本控制结构 1. If
2. Switchif condition1 {
// do something
} else if condition2 {
// do something else
} else {
// catch-all or default
}
if v:= x; v < 0 {
// do something
}
3. forswitch var1 {
case val1:
case var2:
fallthrough
case val3:
f()
default:
...
}
五. Go语言的数据结构 1. 变量和常量for i := 0; i < 100; i++ {
sum += 1
}
for ; sum < 100; {
sum += 10
}
for {
if condition {
break
}
// do something
}
2. 定义示例const identifier type
var identifier type
3. 类型转换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 = true var 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.Println(it.getName()) } // 结构体标签 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)
4. Make 和 Newvar i int = 40
var f float64 = float64(i) 或者 f := float64(i)
六. Go语言函数 1. Go语言传参方式New是返回指针的地址
Make则为返回第一个元素,可预设内存空间,避免未来的内存拷贝
2. Init函数// method 1 fmt.Println("os args is:", os.Args) //method 2 name := flag.String("name", "ribbon", "input you english name") flag.Parse()
3. 传递变长参数Init函数会在包初始化的时候运行
当多个依赖引用统一的项目, 且被引用项目的初始化在init中完成,并且不可重复运行时,会导致启动错误
var myVariable int = 0 func init() { myVariable = 1 }
func append(slice []Type, elems ...Type) []Type
4. 回调函数
func Callback(x int, f func(int, int)) {
f(x, x)
}
5. 方法接口
6. 反射机制方法: 作用在接受者上的函数
func (recv receiver_type) method(parameter_list) (return_value_list)
package main import "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.Println(it.getName()) } g := Human{} g.first = "aa" g.last = "cc" fmt.Println(g.getName()) c = new(Car) c.first1 = "ri" c.last1 = "bbon" fmt.Println(c.getName()) }
7. Go语言中的面向对象编程TypeOf 返回被检查对象的类型
ValueOf返回被检查对象的值
package main import ( "fmt" "reflect" ) type T struct { A string b 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多态 通过接口实现,通过接口定义方法集, 编写多套实现8. Json编解码
9. 错误处理Marshal: 从struct --> string
Unmarshal: 从string --> struct
package main import ( "encoding/json" "fmt" "reflect" ) type Human struct { Name string Age 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 = 30000 h.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 main import ( "encoding/json" "fmt" ) type Human struct { Name string Age int } func structToString(h Human) string { h.Age = 30000 h.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) } } }
10. defer Panic recoverGo没有内置的exception机制, 只提供error接口供定义错误
type error interface {
Error() string
}
可以通过error.New or fmt.Errorf创建新的error
通常应用程序对error的处理大部分是判断error是否为nil
七. 多线程 1. 并发和并行defer这个相当于python java的finally
panic:可以再系统出现不可恢复错误的时候主动调用panic, panic会使当前线程直接crash
recover: 函数从panic或者错误场景中恢复
defer func() { if err := recover(); err != nil { fmt.Println(err) } } () panic("\njson is error\n")
2. 协程
3. CSP协程属于轻量级线程
Go语言在runtime、系统调用等多方面对goroutine调度进行了封装和处理,当遇到长时间执行或者进行系统调用时,会主动把当前goroutine的CPU(P)转让出去, 让其他goroutine能被调度,也就是golang从语言层面支持了协程
每个goroutine默认占用内存远比线程的小(goroutine:2KB 线程:8MB)
goroutine切换开销方面远比线程小(线程涉及切换【从用户态切换到内核态】、16个寄存器、PC/SP等寄存器刷新, goroutine只涉及PC/SP/DX三个寄存器值得修改)
package main import ( "fmt" "time" ) func main() { for i := 0; i < 10; i++ { go fmt.Println(i) } time.Sleep(time.Second) }
4. 线程加锁描述两个独立的并发实体通过共享的通讯channel进行通信的并发模型
协程之间虽然解耦,但是它们和Channel有着耦合
package main import ( "fmt" ) func main() { ch := make(chan int) go func() { ch <- 0 } () fmt.Println(<-ch) }
waitGroup
package main import ( "fmt" "sync" ) type worker struct { in chan int done func() } func dowork(id int, work worker) { for i := range work.in{ work.done() fmt.Printf("%d %c\n", 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.WaitGroup var worker[10] worker for 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 main import ( "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 main import ( "context" "fmt" "time" ) func main() { ctx, cancel := context.WithCancel(context.Background()) go func(ctx context.Context) { for { select { case <-ctx.Done(): fmt.Println("监控退出,停止了...") return default: fmt.Println("goroutine监控中...") time.Sleep(2 * time.Second) } } }(ctx) time.Sleep(10 * time.Second) fmt.Println("可以了,通知监控停止") cancel() //为了检测监控过是否停止,如果没有监控输出,就表示停止了 time.Sleep(5 * time.Second) }
package main func main() { unsafeWrite() } func unsafeWrite() { conflictMap := map[int]int{} for i := 0; i <= 100; i++ { go func() { conflictMap[1] = i }() } }
当多线程对同一个程序进行赋值 *** 作的时候,会导致如下图的异常报错信息,需要添加锁进制在代码中。
GO语言不仅仅提供基于CSP的通讯模型,也支持基于共享内存的多线程数据访问Sync 包提供了锁的基本原语5. 线程调度sync.Mutex互斥锁
package main import ( "fmt" "sync" "time" ) type SafeMap struct { safeMap map[int]int Mutex 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] = i conflictMap.Mutex.Unlock() }() } time.Sleep(time.Second) fmt.Println(conflictMap.safeMap[0], conflictMap.safeMap[1]) } func main() { safeWrite() }
sync.RWMutex 读写分离锁
package main import ( "fmt" "sync" "time" ) type SafeMap struct { safeMap map[int]int Mutex *sync.RWMutex } func safeWrite(conflictMap SafeMap, i int) { conflictMap.Mutex.Lock() conflictMap.safeMap[1] = i conflictMap.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 main import ( "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 main import ( "fmt" "sync" ) func main() { var once sync.Once onceBody := 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 main import ( "fmt" "sync" "time" ) type Queue struct { queue []string cond *sync.Cond } func (q *Queue) Enqueue (item string) { q.cond.L.Lock() defer q.cond.L.Unlock() q.queue = append(q.queue, item) q.cond.Broadcast() } func (q *Queue) Dequeue () string{ q.cond.L.Lock() defer q.cond.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来指定代码仓库或者指定版本
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)