Go 语言小细节(三)


在 for 语句的闭包中使用迭代变量会有问题
在 for 迭代过程中,迭代变量会一直保留,只是每次迭代值不一样。因此在 for 循环中在闭包里直接引用迭代变量,在执行时直接取迭代变量的值,而不是闭包所在迭代的变量值。如果闭包要取所在迭代变量的值,就需要 for 中定义一个变量来保存所在迭代的值,或者通过闭包函数传参。

package main

import (  
    "fmt"
    "time"
)

func forState1(){
    data := []string{"one","two","three"}

    for _,v := range data {
        go func() {
            fmt.Println(v)
        }()
    }
    time.Sleep(3 * time.Second)
    // three, three, three

    for _,v := range data {
        vcopy := v
        // 使用临时变量
        go func() {
            fmt.Println(vcopy)
        }()
    }
    time.Sleep(3 * time.Second)
    // one, two, three

    for _,v := range data {
        go func(in string) {
            fmt.Println(in)
        }(v)
    }
    time.Sleep(3 * time.Second)
    // one, two, three
}

func main() {  
    forState1()
}

再看一个坑埋得比较深的例子。

package main

import (  
    "fmt"
    "time"
)

type field struct {  
    name string
}

func (p *field) print() {  
    fmt.Println(p.name)
}

func main() {  
    data := []field{{"one"},{"two"},{"three"}}
    for _,v := range data {
        // 解决办法:添加如下语句
        // v := v
        go v.print()
    }
    time.Sleep(3 * time.Second)
    // three, three, three

    data2 := []*field{{"one"}, {"two"}, {"three"}}
    // 注意data2是指针数组
    for _, v := range data2 {
        go v.print()
        // go执行是函数,函数执行之前,函数的接受对象已经传过来
    }
    time.Sleep(3 * time.Second)
    // one, two, three
}

defer 语句调用是在当前函数结束之后调用,而不是变量的作用范围。

for _,target := range targets {
    f, err := os.Open(target)
    if err != nil {
        fmt.Println("bad target:",target,"error:",err)
        // too many open files
        break
    }
    defer f.Close()
    // will not be closed at the end of this code block, but closed at end of this function
    // 解决方法1:不用defer
    // f.Close()
    // 解决方法2:将for中的语句添加到匿名函数中执行。
    // func () {
    // }() 
}

nil 值的 interface{} 不等于 nil
Go 中,nil 只能赋值给指针、channel、func、interface、map 或 slice 类型的变量。
interface{} 表示任意类型,可以接收任意类型的值。
interface{} 变量在底是由类型和值两部分组成,表示为(T,V),interface{} 变量比较特殊,判断它是nil时,要求它的类型和值都是 nil,即(nil, nil)。其它类型变量,只要值是 nil,那么此变量就是 nil。声明变量 interface{},它默认就是
nil,底层类型与值表示是(nil, nil)。当任何类型 T 的变量值 V 给 interface{} 变量赋值时,interface{} 变量的底层表示是 (T, V)。只要 T 非 nil,即使 V 是 nil,interface{} 变量也不是 nil。

var data *byte
var in interface{}
fmt.Println(data,data == nil)
// <nil> true
fmt.Println(in,in == nil)
// <nil> true
in = data
fmt.Println(in,in == nil)
// <nil> false
//'data' is 'nil', but 'in' is not 'nil'

doit := func(arg int) interface{} {
    var result *struct{} = nil
    if(arg > 0) {
        result = &struct{}{}
    }
    return result
}
if res := doit(-1); res != nil {
    fmt.Println("good result:",res)
}

doit = func(arg int) interface{} {
    var result *struct{} = nil
    if(arg > 0) {
        result = &struct{}{}
    } else {
        return nil
    }
    return result
}

if res := doit(-1); res != nil {
    fmt.Println("good result:",res)
} else {
    fmt.Println("bad result (res is nil)")
}

GOMAXPROCS、Concurrency 和 Parallelism
Go 1.5 开始可以设置执行上下文的数量为 CPU 内核数量 runtime.NumCPU(),也可以通过 GOMAXPROCS 环境变量来设置,还可调用 runtime.GOMAXPROCS() 函数来设置。注意,GOMAXPROCS 并不代表 Go 运行时能够使用的 CPU 数量,它是一个小 256 的数值,可以设置比实际的 CPU 数量更大的数字。

fmt.Println(runtime.GOMAXPROCS(-1))
// X (1 on play.golang.org)
fmt.Println(runtime.NumCPU())
// X (1 on play.golang.org)
runtime.GOMAXPROCS(20)
fmt.Println(runtime.GOMAXPROCS(-1))
// 20
runtime.GOMAXPROCS(300)
fmt.Println(runtime.GOMAXPROCS(-1))
// 256
分享:

评论