您的位置:首页 > 房产 > 家装 > Golang的基本使用

Golang的基本使用

2024/10/6 18:27:25 来源:https://blog.csdn.net/qq2904529388/article/details/139100196  浏览:    关键词:Golang的基本使用

目录

变量的声明

Golang常用容器

defer

有趣的多态

结构体标签和reflect 反射

Golang最强的协程

channel

go可能造成的内存泄露

变量的声明

方法 1:有类型,有var,不赋值 在Golang中默认值为0

方法 2:无类型,有var,赋值

方法 3:无类型,无var,赋值

多变量声明

多变量声明,无类型无var

如下

package mainimport ("fmt"
)func main() {//var 用于声明变量var a int // 方法 1:有类型,有var,不赋值 在Golang中默认值为0fmt.Println("a=", a)var b = 0 //方法 2:无类型,有var,赋值fmt.Println("b=", b)c := 0 //方法 3:无类型,无var,赋值fmt.Println("c=", c)//多变量声明var d, e int = 1, 2 //fmt.Println("d=", d, "e=", e)//多变量声明,无类型无varf, g := "string", 3fmt.Println("f=", f, "e=", g)}

常量 

定义后无法改变

Golang常用容器

静态数组(slice)

package mainimport "fmt"func main() {//数组,静态数组var arr [5]intarr[0] = 1arr[1] = 2arr[2] = 3arr[3] = 4arr[4] = 5fmt.Println(arr)      // 输出: [1 2 3 4 5]fmt.Println(len(arr)) // 输出: 5}

动态数组(array)

跟静态数组的区别,就是 不用规定数组大小,自动分配自动扩容

package mainimport "fmt"func main() {//跟静态数组的区别,就是 不用规定数组大小,自动分配自动扩容slice := []int{1, 2, 3, 4, 5}fmt.Println(slice)      // 输出: [1 2 3 4 5]fmt.Println(len(slice)) // 输出: 5
}

自动扩容演示

slice两倍扩容

package mainimport "fmt"func main() {//跟静态数组的区别,就是 不用规定数组大小,自动分配自动扩容slice := []int{1, 2, 3, 4, 5}fmt.Println(slice)      // 输出: [1 2 3 4 5]fmt.Println(len(slice)) // 输出: 5fmt.Println(cap(slice)) //当前容量为5slice = append(slice, 6)fmt.Println(len(slice)) // 输出: 数据数量=6fmt.Println(cap(slice)) // 输出: 10 slice两倍扩容
}

字符串(string)

package mainimport "fmt"func main() {str := "Hello, World!"fmt.Println(str)         // 输出: Hello, World!fmt.Println(len(str))    // 输出: 13fmt.Printf("%s", str[0]) // 输出: H 
}

映射(map)

package mainimport "fmt"func main() {m := make(map[int]string)m[1] = "apple"m[2] = "banana"m[3] = "orange"for i := 1; i < 4; i++ {fmt.Printf("m=%s\n", m[i])}
}

defer

类似c++中的析构函数

defer作用:

        释放占用的资源

        捕捉处理异常

        输出日志

func main() {defer func() {fmt.Println("析构函数")}()fmt.Println("main start")//===//业务//===fmt.Println("main off")
}

当然Golang中有资源自动回收,不用自己析构,当然有特殊的例子下面讲了协程再说

有趣的多态

import "fmt"// 两个类
type Student struct {age int
}type Programmer struct {age int
}// 同一调用
func whatJob(p Person) {p.job()
}func growUp(p Person) {p.growUp()
}// 一个万能接口
type Person interface { //intetface 万能变量job()growUp()
}// Student 函数方法
func (p Student) job() {fmt.Println("I am a student.")return
}func (p *Student) growUp() {p.age += 1return
}// Programmer 函数方法
func (p Programmer) job() {fmt.Println("I am a programmer.")return
}func (p *Programmer) growUp() {p.age += 10return
}func main() {qcrao := Student{age: 18}whatJob(&qcrao)growUp(&qcrao)fmt.Println(qcrao)stefno := Programmer{age: 100}whatJob(&stefno)growUp(&stefno) //同一个函数,调用结果不同fmt.Println(stefno)
}

结构体标签和reflect 反射

import ("fmt""reflect"
)type resume struct {Name string `json:"电影" doc:"喜剧之王"`
}func findDoc(stru interface{}) map[string]string {t := reflect.TypeOf(stru).Elem() //reflect 反射 逆推类型doc := make(map[string]string)for i := 0; i < t.NumField(); i++ {doc[t.Field(i).Tag.Get("json")] = t.Field(i).Tag.Get("doc")}return doc}func main() {var stru resumedoc := findDoc(&stru)fmt.Printf("电影字段=%s\n", doc["电影"])
}

Golang最强的协程

package mainimport ("fmt""time"
)func main() {go func() {fmt.Println("我是协程")}()fmt.Println("我是主协程")time.Sleep(1 * time.Second) // 等待一秒钟,让协程有足够的时间执行
}

身为一个cpp学者,当看见Golang写协程那么方便,异常兴奋

那么协程用什么与主协程通信呢?

channel

管道(channel)接收和发送数据都是阻塞的

package mainimport ("fmt""time"
)func main() {c := make(chan int) //创建管道(channel),而且接收和发送数据都是阻塞的go func() {fmt.Println("我是协程")c <- 666 //向管道内写入666}()a := <-c //管道类读出fmt.Println("我是主协程")fmt.Println("a=", a)time.Sleep(1 * time.Second) // 等待一秒钟,让协程有足够的时间执行
}

go可能造成的内存泄露

func main() {ch := func() <-chan int {ch := make(chan int)go func() {for i := 0; ; i++ {ch <- i}} ()return ch}()for v := range ch {fmt.Println(v)if v == 5 {break}}
}

使用 valgrind 运行 程序,发现 协程内存并没有回收

问题:

上面的程序中后台Goroutine向管道输入自然数序列,main函数中输出序列。

但是当break跳出for循环的时候,后台Goroutine就处于无法被回收的状态了。

因为 Goroutine还在向管道中写数据,主协程已经退出

改正:

使用context

package mainimport ("context""fmt"
)func main() {//WithCancel(ctx Context, cancel CancelFunc)=(名 Context,处理函数 CancelFunc)ctx, cancel := context.WithCancel(context.Background()) //context.Background() 处理 Goroutinech := func(ctx context.Context) <-chan int {ch := make(chan int)go func() {for i := 0; ; i++ {select {case <-ctx.Done():returncase ch <- i:}}}()return ch}(ctx)for v := range ch {fmt.Println(v)if v == 5 {cancel()break}}
}

当main函数在break跳出循环时,通过调用 context 来通知后台Goroutine退出

这样就避免了Goroutine的泄漏。 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com