使用 Go 语言常遇到的问题 | 青训营

103 阅读5分钟

作为一个参加青训营的学员,我在这段时间内深入学习了Go语言的进阶知识。在这篇学习笔记中,我将总结我所学到的知识点,并分享一些学习心得和建议。

在学习Go语言进阶之后,我开始下载软件并进行部署。都说开头难,起步的过程真的很吐血,配置环境时遇到各种问题。

首先我学习了Go语言的并发编程。在Go语言中,使用goroutine和channel可以轻松地实现并发编程。通过使用goroutine,我们可以让多个任务同时执行,而不需要手动控制线程。同时,使用channel可以让多个goroutine之间进行通信和同步。这些工具使得Go语言在处理高并发的情况下表现出色。

其次学习了Go语言的反射机制。反射机制可以让我们在运行时动态地获取和修改变量的值和类型。这个特性在某些场景下非常有用,比如编写通用的函数或者需要动态地创建对象。

另外,在Go语言中,错误处理是非常重要的一部分。通过使用error类型和panic/recover机制,我们可以在程序出现错误时进行恰当的处理和恢复。

以下为总结的使用 Go 语言常遇到的问题: A.1可变参数是空接口类型

当参数的可变参数是空接口类型时,传入空接口的切片时需要注意参数展开的问题:

func main(){

vara= []interface{}{1,2, 3}

fmt.PrintIn(a) fmt.Printin(a...)

不管是否展开,编译器都无法发现错误,但是输出是不同的:[12 3]123

A.2数组是值传递

在函数调用参数中,数组是值传递,无法通过修改数组类型的参数返回结果; func main() {

x := [3]int{1,2,3}

func(arr [3]int){

arr[0]=7

fmt.PrintIn(arr)}(x)

fmt.println(x)  355 

必要时需要使用切片。

..3 map 遍历时顺序不固定

map 是一种散列表实现,每次遍历的顺序都可能不一样: func main() {

m:-map[stringlstringl

"1":"1","2":"2","3":"3",}

for k, v :m range m (

println(k,v)}

出现       }     

A.4返回值被屛蔽

在局部作用域中,命名的返回值被同名的局部变量屏蔽:

func Foo() (err error){

if err := Bar(); err != nil {

return

return}

A.5 recover()必须在defer函数中运行

recover()捕获的是祖父级调用时的异常,直接调用是无效的: func main() {

recover() panic(1)

直接调用defer 也是无效的: func main() {

defer recover() panic(1)}

defer调用时多层嵌套依然无效:

func main()(

defer func()(

解决自

fune(){ recover())()}()

FunC

panic(1)

必须在defer函数中直接调用才有效: func main() (

defer func() {

recover()}()

panic(1)}

A.6 main()函数提前退出

后台Goroutine 无法保证完成任务: func main() {

go printin("hello")

A.7 通过sleep()来回避并发中的问题

休眠并不能保证输出完整的字符串: func main() {

go printin("hello")

time.Sleep(time.Second)

func main() {  VOD9t 2.A    

类似的还有通过插入调度语句:

go printin("hello") runtime.Gosched()}

A.8独占CPU导致其他Goroutine饿死

Goroutine 是协作式抢占调度,Goroutine 本身不会主动放弃 CPU: func main() {

runtime.GOMAXPROCS(1)

go func(){

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

fmt,printin(i)

A.9 不同 Goroutine之间不满足顺序一致性内存模型           

 

for () // 占用CPU

解决的方法是在for循环中加入runtime.Gosched()调度函数: func main() (

runtime.GOMAXPROCS(1) go func(){

for i := 0; i < 10; i++ (

fmt.Println(i)

)

}() for {

runtime.Gosched()

 

或者是通过阻塞的方式避免 CPU 占用:

func main(){

runtime.GOMAXPROCS(1)

go func(){

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

fmt.Println(i)}

os.Exit(0)}()

A.12

select{}

 

A.9 不同Goroutine之间不满足顺序一致性内存模型

因为在不同的Goroutine,main()函数中无法保证能打印出“hello,world”:

var msg string var done bool

func setup(){

msg = "hello, world" done=true

 

func main()(

go setup()

for idone ( printin(msg)}

解决的办法是用显式同步:

var msg string

var done - make(chan bool)

func setup()(

msg - "hello, world" done <- true

func main(){

go setup()<-done

printin(msg)

msg的写入是在通道发送之前,所以能保证打印“hello,world”。 A.10错引用同一量

下面的代码最终将输出相同的值: func main() {

for i := 0; i < 5; i++ [

defer func(){

println(i)}()}}

改进的方法是在每轮迭代中生成一个局部变量: func main() {

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

i := i

defer func() {

println(i)}()

 

或者是通过函数参数传入: func main() {

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

defer func(i int) {

println(i)

最后,我想分享一些学习心得和建议。首先最重要的是实践。通过写代码来巩固所学知识,这样才能更好地理解和掌握。其次要善于查阅文档和资料。Go语言社区非常活跃,有很多高质量的文档和资料可供参考,例如最近在掘金社区看到一篇有关“并发编程”的文章。最后,要积极参与讨论和交流。在青训营的学习中,我发现与其他同学的交流和讨论非常有助于加深理解和解决问题。

总之,在这段时间内,我对Go语言的理解和掌握有了很大的提升。我相信这些知识和经验对我未来的工作和学习都会有很大的帮助。对于其他入门同学,我建议多加实践、查阅文档和资料,并积极参与讨论和交流。相信通过不断地学习和实践,我们都能够成为优秀的Go语言开发者。