总结Golang开发时容易掉的坑
make和append同时用的问题
golang支持切片,可以append追加元素,十分方便。
make是用于初始化切片的函数,但如果对其没有理解清楚,就容易出错。
错误示例
这是一个初始化切片的代码。
package main
import "fmt"
func main() {
values:=make([]int,3)
values=append(values,1)
fmt.Println(values) // 输出 [0 0 0 1]
}
因为make后的切片已经初始化了长度为3
,此时再append
会追加到后面,就多此一举了。
因此,在Golang里直接初始化空切片更方便。
正确写法
values:=[]int{}
values=append(values,1)
无法更新的数组
错误示例
package main
import "fmt"
func main() {
data := []int{1, 2, 3}
for _, v := range data {
v += 1 //原始元素未更改
}
fmt.Println(data) //输出 [1 2 3]
}
我们会发现,数组元素并未更改。
这是因为for
循环中的v
,是一个元素的拷贝。
如果需要修改,应当使用下标,或使用指针数组。
正确写法
package main
import "fmt"
func main() {
data := []int{1, 2, 3}
for i := range data {
data[i] += 1
}
fmt.Println(, data) //输出 [10 20 30]
}
并发问题
错误示例
下面的代码应该是希望将数组下标并行打印。
package main
import "fmt"
import "time"
func main() {
values:=[]int{1,2,3,4,5}
for idx := range values {
go func() {
fmt.Println(idx)
}()
}
time.Sleep(3*time.Second)
}
但我们发现程序并没有和想象的那样输出序列,而是输出了4
。这是因为协程并没有立刻执行,而在执行的时候,idx
已经被赋值成了4
。
正确写法
将 val
作为一个参数传入 goroutine 中,每个 val 都会被独立计算并保存到 goroutine 的栈中,从而得到预期的结果。
package main
import "fmt"
import "time"
func main() {
values:=[]int{0,1,2,3,4}
for idx := range values {
go func(idx int) {
fmt.Println(idx)
}(idx)
}
time.Sleep(3*time.Second)
}
此外,在循环里,将参数赋值一次,也可以达到同样效果。但从程序美观的角度考虑,建议使用上面的方式。
package main
import "fmt"
import "time"
func main() {
values:=[]int{0,1,2,3,4}
for idx := range values {
x:=idx
go func() {
fmt.Println(x)
}()
}
time.Sleep(3*time.Second)
}
没有被捕获的error
通常,为了捕获错误,然后进行处理,我们会在函数中增加一个defer
。
错误示例
下面的代码中,defer
并未捕获到error
。
package main
import "fmt"
func main() {
var err error
defer fmt.Printf("error is [%v]\n",err)
err = fmt.Errorf("test error")
fmt.Println(err)
}
输出
test error
error is []
这是因为defer
后跟的函数的传参是复制传参,而这个动作在执行到defer
这条语句时就做了,并不是在函数结束时处理的。
这和并发时的闭包问题类似,但这里我们希望它拿到最后的error
信息。因此需要这样处理。
正确写法
package main
import "fmt"
func main() {
var err error
defer func(){
fmt.Printf("error is [%v]\n",err)
}()
err = fmt.Errorf("test error")
fmt.Println(err)
}
没有执行完的协程
Golang提供了非常方便的并发开发,使用go
语句,就可以开启一个协程。然后可以通过WaitGroup
来控制并发等待。
错误示例
下面的代码似乎想并行输出数组下标,并且做了协程等待。
但实际上这个程序却没有输出任何内容(也可能会输出,存在偶然情况),这是为什么呢?
package main
import "fmt"
import "sync"
func main() {
wg:=&sync.WaitGroup{}
values:=[]int{1,2,3,4,5}
for i := range values {
// wg.Add(1) 正确的位置
go func(i int,wg *sync.WaitGroup){
wg.Add(1) // 错误的位置
defer wg.Done()
fmt.Println(i)
}(i,wg)
}
wg.Wait()
}
代码里,最大的问题是wg.Add
的位置,它不应该放到协程中。协程不一定是立刻执行的,因此这可能会导致wg.Wait
先执行,直接就退出了。正确的写法应该将其放到go
语句的前面。
而如果运气好的话,也可能会输出部分内容。
暂无评论内容