Go语言快速入门

阅读: 评论:0

Go语言快速入门

Go语言快速入门

一. 下载GO

  • go安装文件以及源代码

Downloads - The Go Programming Language

  • 下载对应平台的二进制文件以及安装
  • 环境变量

        GOROOT: go的安装目录

        GOPATH: src 存放源代码

                            pkg 存放依赖包

                            bin 存放可执行文件

        其他常用变量: GOOS GOARCH GOPROXY

        GOPROXY建议使用: export GOPROXY=

二. IDE设置

下载并安装 visual studio code

Visual Studio Code - Code Editing. Redefined

安装go语言插件

Go - Visual Studio Marketplace

三.GO语言一些基本命令

  • 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

四. Go语言的基本控制结构

1. If

if condition1 {

        // do something

} else if condition2 { 

        // do something else 

} else {

        // catch-all or default

}

if v:= x; v < 0 {

        // do something

}

2. Switch

switch var1 {

        case val1:

        case var2:

                fallthrough

        case val3:

                f()

        default:

                ...

}

3. for

for i := 0; i < 100; i++ {

        sum += 1

for ; sum < 100; {

        sum += 10

}

for {

        if condition {

                break

        }

        // do something

}

五. Go语言的数据结构

1. 变量和常量

const identifier type

var identifier type

2. 定义示例

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)

3. 类型转换

var i int = 40

var f float64 = float64(i) 或者 f := float64(i)

4. Make 和 New

New是返回指针的地址

Make则为返回第一个元素,可预设内存空间,避免未来的内存拷贝

六. Go语言函数

1. Go语言传参方式

// method 1
fmt.Println("os args is:", os.Args)//method 2
name := flag.String("name", "ribbon", "input you english name")
flag.Parse()

2. Init函数

Init函数会在包初始化的时候运行

当多个依赖引用统一的项目, 且被引用项目的初始化在init中完成,并且不可重复运行时,会导致启动错误

var myVariable int = 0func init() {myVariable = 1
}

3. 传递变长参数

func append(slice []Type, elems ...Type) []Type

4. 回调函数

func Callback(x int, f func(int, int)) {f(x, x)
}

5. 方法接口

方法: 作用在接受者上的函数

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())
}

6. 反射机制

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))
}

7. Go语言中的面向对象编程

  • 可见性控制
    • public - 常量、变量、类型、接口、结构、函数等的名称大写
    • private - 非大写的只能包內使用
  • 继承
    • 通过组合实现, 内嵌一个或者多个struct
  • 多态
    • 通过接口实现,通过接口定义方法集, 编写多套实现

8. Json编解码

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)}}
}

9. 错误处理

Go没有内置的exception机制, 只提供error接口供定义错误

type error interface {

        Error() string

}

可以通过error.New or fmt.Errorf创建新的error

通常应用程序对error的处理大部分是判断error是否为nil

10. defer Panic recover

defer这个相当于python java的finally

panic:可以再系统出现不可恢复错误的时候主动调用panic, panic会使当前线程直接crash

recover: 函数从panic或者错误场景中恢复

defer func() {if err := recover(); err != nil {fmt.Println(err)}
} ()
panic("njson is errorn")

七. 多线程

1. 并发和并行

2. 协程

协程属于轻量级线程

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)
}

3. CSP

描述两个独立的并发实体通过共享的通讯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)
}

4. 线程加锁

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)
}

5. 线程调度

进程: 资源分配的基本单位

线程: 调度的基本单位

无论是线程还是进程,在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 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23