作为一个参加青训营的学员,我在这段时间内深入学习了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语言开发者。