参考:
示例:
package main
import (
"runtime"
"time"
)
var _ = runtime.GOMAXPROCS(3)
var a, b int
func u1() {
a = 1
b = 2
}
func u2() {
a = 3
b = 4
}
func p() {
println(a)
println(b)
}
func main() {
go u1()
go u2()
go p()
time.Sleep(1 * time.Second)
}
1
2
3
4
0
2 // 可见b 先于a被更新
0
0
1
4 // 两个goroutine相互影响
Go 1.4 : uses only one execution context / OS thread,only one goroutine can execute at any given time.
Go 1.5 : Go sets the number of execution contexts to the number of logical CPU cores returned by runtime.NumCPU().
在C++ 中:
使用new显式指定创建堆对象
不可以返回局部变量的引用
在Go中:
由编译器决定而不是使用new() 或者 make()指定。
可以返回局部变量的引用
# 示例
package main
import "fmt"
func main() {
var data *byte
var in interface{}
fmt.Println(data,data == nil) //prints: <nil> true
fmt.Println(in,in == nil) //prints: <nil> true
in = data
fmt.Println(in,in == nil) //prints: <nil> false
//'data' is 'nil', but 'in' is not 'nil'
}
# 错误: 接口不是nil,但接口的值可能是nil
package main
import "fmt"
func main() {
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) //prints: good result: <nil>
//'res' is not 'nil', but its value is 'nil'
}
}
# 正确:显式返回nil,保证值为空时可以通过接口为空进行判断
package main
import "fmt"
func main() {
doit := func(arg int) interface{} {
var result *struct{} = nil
if(arg > 0) {
result = &struct{}{}
} else {
return nil //return an explicit 'nil'
}
return result
}
if res := doit(-1); res != nil {
fmt.Println("good result:",res)
} else {
fmt.Println("bad result (res is nil)") //here as expected
}
}
- 不可直接更新map 的val(struct 类型)的字段值
# 错误
package main
type data struct {
name string
}
func main() {
m := map[string]data {"x":{"one"}}
m["x"].name = "two" //error //错误原因: map elements are not addressable.
}
# 对比: slice 元素是可寻址的.
package main
import "fmt"
type data struct {
name string
}
func main() {
s := []data {{"one"}}
s[0].name = "two" //ok
fmt.Println(s) //prints: [{two}]
}
# 正确: 使用临时变量取出map的value (struct 类型),再重新赋值
package main
import "fmt"
type data struct {
name string
}
func main() {
m := map[string]data {"x":{"one"}}
r := m["x"]
r.name = "two"
m["x"] = r
fmt.Printf("%v",m) //prints: map[x:{two}]
}
# 正确: 使用指针类型value (struct 类型)时可以正常寻址
package main
import "fmt"
type data struct {
name string
}
func main() {
m := map[string]*data {"x":{"one"}}
m["x"].name = "two" //ok
fmt.Println(m["x"]) //prints: &{two} // 注意 key "x" 不存在时,可能导致空指针panic
}
- 指针接收方法可赋予值类型,不可赋予接口或map类型
package main
import "fmt"
type data struct {
name string
}
func (p *data) print() {
fmt.Println("name:",p.name)
}
type printer interface {
print()
}
func main() {
d1 := data{"one"}
d1.print() //ok
var in printer = data{"two"} //error
in.print()
m := map[string]data {"x":data{"three"}}
m["x"].print() //error
}
# 错误
func First(query string, replicas ...Search) Result {
c := make(chan Result)
searchReplica := func(i int) { c <- replicas[i](query) }
for i := range replicas {
go searchReplica(i)
}
return <-c
}
# 正确: 使用足够大的缓存,确保所有的goroutines都退出,资源没有泄漏
func First(query string, replicas ...Search) Result {
c := make(chan Result,len(replicas))
searchReplica := func(i int) { c <- replicas[i](query) }
for i := range replicas {
go searchReplica(i)
}
return <-c
}
# 正确: 使用select 语句并添加default
func First(query string, replicas ...Search) Result {
c := make(chan Result,1)
searchReplica := func(i int) {
select {
case c <- replicas[i](query):
default:
}
}
for i := range replicas {
go searchReplica(i)
}
return <-c
}
# 正确: 使用 cancel 消息channel
func First(query string, replicas ...Search) Result {
c := make(chan Result)
done := make(chan struct{})
defer close(done)
searchReplica := func(i int) {
select {
case c <- replicas[i](query):
case <- done:
}
}
for i := range replicas {
go searchReplica(i)
}
return <-c
}
# 错误
package main
import "fmt"
func main() {
var data interface{} = "great"
if data, ok := data.(int); ok {
fmt.Println("[is an int] value =>",data)
} else {
fmt.Println("[not an int] value =>",data)
//prints: [not an int] value => 0 (not "great")
}
}
#正确
package main
import "fmt"
func main() {
var data interface{} = "great"
if res, ok := data.(int); ok {
fmt.Println("[is an int] value =>",res)
} else {
fmt.Println("[not an int] value =>",data)
//prints: [not an int] value => great (as expected)
}
}
- defer 语句会在被包含的函数执行结束时执行,而不是被包含的右大括号处
# 错误
package main
import (
"fmt"
"os"
"path/filepath"
)
func main() {
if len(os.Args) != 2 {
os.Exit(-1)
}
start, err := os.Stat(os.Args[1])
if err != nil || !start.IsDir(){
os.Exit(-1)
}
var targets []string
filepath.Walk(os.Args[1], func(fpath string, fi os.FileInfo, err error) error {
if err != nil {
return err
}
if !fi.Mode().IsRegular() {
return nil
}
targets = append(targets,fpath)
return nil
})
for _,target := range targets {
f, err := os.Open(target)
if err != nil {
fmt.Println("bad target:",target,"error:",err) //prints error: too many open files
break
}
defer f.Close() //will not be closed at the end of this code block
//do something with the file...
}
}
#正确
package main
import (
"fmt"
"os"
"path/filepath"
)
func main() {
if len(os.Args) != 2 {
os.Exit(-1)
}
start, err := os.Stat(os.Args[1])
if err != nil || !start.IsDir(){
os.Exit(-1)
}
var targets []string
filepath.Walk(os.Args[1], func(fpath string, fi os.FileInfo, err error) error {
if err != nil {
return err
}
if !fi.Mode().IsRegular() {
return nil
}
targets = append(targets,fpath)
return nil
})
for _,target := range targets {
func() {
f, err := os.Open(target)
if err != nil {
fmt.Println("bad target:",target,"error:",err)
return
}
defer f.Close() //ok
//do something with the file...
}()
}
}
- defer 语句中包含的变量,会在defer语句处赋值,而不是执行defer语句时赋值。
package main
import "fmt"
func main() {
var i int = 1
defer fmt.Println("result =>",func() int { return i * 2 }())
i++
//prints: result => 2 (not ok if you expected 4)
}
#错误
package main
import (
"fmt"
"time"
)
func main() {
data := []string{"one","two","three"}
for _,v := range data {
go func() {
fmt.Println(v)
}()
}
time.Sleep(3 * time.Second)
//goroutines print: three, three, three
}
#正确
package main
import (
"fmt"
"time"
)
func main() {
data := []string{"one","two","three"}
for _,v := range data {
vcopy := v //
go func() {
fmt.Println(vcopy)
}()
}
time.Sleep(3 * time.Second)
//goroutines print: one, two, three
}
#正确
package main
import (
"fmt"
"time"
)
func main() {
data := []string{"one","two","three"}
for _,v := range data {
go func(in string) {
fmt.Println(in)
}(v)
}
time.Sleep(3 * time.Second)
//goroutines print: one, two, three
}
#错误
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 {
go v.print()
}
time.Sleep(3 * time.Second)
//goroutines print: three, three, three
}
#正确
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)
//goroutines print: one, two, three
}
#错误
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 {
go v.print()
}
time.Sleep(3 * time.Second)
}
// goroutines print: one, three, two
package main
import "fmt"
func main() {
loop:
for {
switch {
case true:
fmt.Println("breaking out...")
break loop
}
}
fmt.Println("out!")
}
#错误
package main
import "sync"
type myMutex sync.Mutex
func main() {
var mtx myMutex
mtx.Lock() //错误,新类型并没有原类型的方法
mtx.Unlock() //错误
}
# 匿名结构体,实现方法继承
package main
import "sync"
type myLocker struct {
sync.Mutex
}
func main() {
var lock myLocker
lock.Lock() //ok
lock.Unlock() //ok
}
#接口赋值转换,也可以保留接口的方法
package main
import "sync"
type myLocker sync.Locker
func main() {
var lock myLocker = new(sync.Mutex)
lock.Lock() //ok
lock.Unlock() //ok
}
package main
import (
"fmt"
"net/http"
"io/ioutil"
)
func main() {
tr := &http.Transport{DisableKeepAlives: true}
client := &http.Client{Transport: tr}
resp, err := client.Get("http://golang.org")
if resp != nil {
defer resp.Body.Close()
}
if err != nil {
fmt.Println(err)
return
}
fmt.Println(resp.StatusCode)
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(len(string(body)))
}
package main
import (
"fmt"
"net/http"
"io/ioutil"
)
func main() {
req, err := http.NewRequest("GET","http://golang.org",nil)
if err != nil {
fmt.Println(err)
return
}
req.Close = true // 在请求完成后关闭连接
// 或者下面方法,server端会回复 Connection: close,客户端收到close 关闭连接
// req.Header.Add("Connection", "close")
resp, err := http.DefaultClient.Do(req)
if resp != nil {
defer resp.Body.Close()
}
if err != nil {
fmt.Println(err)
return
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(len(string(body)))
}
- 关闭 HTTP Response Body 的正确方法
package main
import (
"fmt"
"net/http"
"io/ioutil"
)
func main() {
resp, err := http.Get("https://api.ipify.org?format=json")
if resp != nil { // 单独判断非nil,而不是依赖err是否为nil
defer resp.Body.Close()
}
if err != nil {
fmt.Println(err)
return
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
# 除了 map 或 slice,一般对值接收者操作不会变更其原始值
package main
import "fmt"
type data struct {
num int
key *string
items map[string]bool
}
func (this *data) pmethod() {
this.num = 7
}
func (this data) vmethod() {
this.num = 8
*this.key = "v.key"
this.items["vmethod"] = true
}
func main() {
key := "key.1"
d := data{1,&key,make(map[string]bool)}
fmt.Printf("num=%v key=%v items=%v\n",d.num,*d.key,d.items)
//prints num=1 key=key.1 items=map[]
d.pmethod()
fmt.Printf("num=%v key=%v items=%v\n",d.num,*d.key,d.items)
//prints num=7 key=key.1 items=map[]
d.vmethod()
fmt.Printf("num=%v key=%v items=%v\n",d.num,*d.key,d.items)
//prints num=7 key=v.key items=map[vmethod:true]
}
# 出错 fatal error: all goroutines are asleep - deadlock!
package main
import (
"fmt"
"time"
)
func main() {
var ch chan int // 空channel (可以用make初始化)
for i := 0; i < 3; i++ {
go func(idx int) {
ch <- (idx + 1) * 2
}(i)
}
//get first result
fmt.Println("result:",<-ch)
//do other work
time.Sleep(2 * time.Second)
}
# 利用以上特性,交替enable 或 disable case 语句
package main
import "fmt"
import "time"
func main() {
inch := make(chan int)
outch := make(chan int)
go func() {
var in <- chan int = inch
var out chan <- int
var val int
for {
select {
case out <- val:
out = nil
in = inch
case val = <- in:
out = outch
in = nil
}
}
}()
go func() {
for r := range outch {
fmt.Println("result:",r)
}
}()
time.Sleep(0)
inch <- 1
inch <- 2
time.Sleep(3 * time.Second)
}
- 向已经关闭的channel发送数据将导致panic
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
for i := 0; i < 3; i++ {
go func(idx int) {
ch <- (idx + 1) * 2
}(i)
}
//get the first result
fmt.Println(<-ch)
close(ch) //not ok (you still have other senders)
//do other work
time.Sleep(2 * time.Second)
}
# 增加一个消息channel通知关闭消息,停止发送
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
done := make(chan struct{})
for i := 0; i < 3; i++ {
go func(idx int) {
select {
case ch <- (idx + 1) * 2: fmt.Println(idx,"sent result")
case <- done: fmt.Println(idx,"exiting")
}
}(i)
}
//get first result
fmt.Println("result:",<-ch)
close(done)
//do other work
time.Sleep(3 * time.Second)
}
-
向无缓冲 Channel 发送数据,在接收端状态Ready后立即返回
package main
import "fmt"
func main() {
ch := make(chan string)
go func() {
for m := range ch {
fmt.Println("processed:",m)
}
}()
ch <- "cmd.1"
ch <- "cmd.2" //won't be processed
}
-
WaitGroup 等待 goroutine 结束
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
done := make(chan struct{})
wq := make(chan interface{})
workerCount := 2
for i := 0; i < workerCount; i++ {
wg.Add(1)
go doit(i,wq,done,&wg)
}
for i := 0; i < workerCount; i++ {
wq <- i
}
close(done)
wg.Wait()
fmt.Println("all done!")
}
func doit(workerId int, wq <-chan interface{},done <-chan struct{},wg *sync.WaitGroup) {
fmt.Printf("[%v] is running\n",workerId)
defer wg.Done()
for {
select {
case m := <- wq:
fmt.Printf("[%v] m => %v\n",workerId,m)
case <- done:
fmt.Printf("[%v] is done\n",workerId)
return
}
}
}
[1] is running
[1] m => 0
[0] is running
[0] is done
[1] m => 1
[1] is done
all done!
package main
import (
"fmt"
"time"
)
func main() {
workerCount := 2
for i := 0; i < workerCount; i++ {
go doit(i)
}
time.Sleep(1 * time.Second)
fmt.Println("all done!")
}
func doit(workerId int) {
fmt.Printf("[%v] is running\n",workerId)
time.Sleep(3 * time.Second)
fmt.Printf("[%v] is done\n",workerId)
}
[0] is running
[1] is running
all done!
package main
import (
"fmt"
"encoding/json"
)
type MyData struct {
One int
two string
}
func main() {
in := MyData{1,"two"}
fmt.Printf("%#v\n",in) //prints main.MyData{One:1, two:"two"}
encoded,_ := json.Marshal(in)
fmt.Println(string(encoded)) //prints {"One":1}
var out MyData
json.Unmarshal(encoded,&out)
fmt.Printf("%#v\n",out) //prints main.MyData{One:1, two:""}
}
#错误
package main
import "fmt"
func main() {
data := []int{1,2,3}
i := 0
++i //error
fmt.Println(data[i++]) //error
}
#正确
package main
import "fmt"
func main() {
data := []int{1,2,3}
i := 0
i++
fmt.Println(data[i])
}
# 空 case 语句自动退出,不会顺延到下一个case
package main
import "fmt"
func main() {
isSpace := func(ch byte) bool {
switch(ch) {
case ' ': //error
case '\t':
return true
}
return false
}
fmt.Println(isSpace('\t')) //prints true (ok)
fmt.Println(isSpace(' ')) //prints false (not ok)
}
# 支持多case项
package main
import "fmt"
func main() {
isSpace := func(ch byte) bool {
switch(ch) {
case ' ', '\t':
return true
}
return false
}
fmt.Println(isSpace('\t')) //prints true (ok)
fmt.Println(isSpace(' ')) //prints true (ok)
}
- Slice, Array, and Map 多行时,逗号的使用
#出错
package main
func main() {
x := []int{
1,
2 //error
}
_ = x
}
# 正确
package main
func main() {
x := []int{
1,
2,
}
x = x
y := []int{3,4,} //no error
y = y
}
# len返回bytes的个数,而不是字符的个数
package main
import "fmt"
func main() {
data := ""
fmt.Println(len(data)) //prints: 3
}
#获取字符的个数
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
data := ""
fmt.Println(utf8.RuneCountInString(data)) //prints: 1
}
#严格来说,一个字符可能由多个rune组成
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
data := "é"
fmt.Println(len(data)) //prints: 3
fmt.Println(utf8.RuneCountInString(data)) //prints: 2
}
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
data1 := "ABC"
fmt.Println(utf8.ValidString(data1)) //prints: true
data2 := "A\xfeC"
fmt.Println(utf8.ValidString(data2)) //prints: false
}
package main
import "fmt"
func main() {
x := "text"
fmt.Println(x[0]) //print 116
fmt.Printf("%T",x[0]) //prints uint8,可见索引取字符串,值类型为uint8
}
#错误
package main
import "fmt"
func main() {
x := "text"
x[0] = 'T'
fmt.Println(x)
}
#正确
package main
import "fmt"
func main() {
x := "text"
xbytes := []byte(x)
xbytes[0] = 'T'
fmt.Println(string(xbytes)) //prints Text
}
#错误
package main
import "fmt"
func main() {
x := map[string]string{"one":"a","two":"","three":"c"}
if v := x["two"]; v == "" { //incorrect
fmt.Println("no entry")
}
}
#正确
package main
import "fmt"
func main() {
x := map[string]string{"one":"a","two":"","three":"c"}
if _,ok := x["two"]; !ok {
fmt.Println("no entry")
}
}
# 默认取索引值
package main
import "fmt"
func main() {
x := []string{"a","b","c"}
for v := range x {
fmt.Println(v) //prints 0, 1, 2
}
}
#不接收索引,只获取值
package main
import "fmt"
func main() {
x := []string{"a","b","c"}
for _, v := range x {
fmt.Println(v) //prints a, b, c
}
}
# 数组通过值拷贝传递参数,函数内赋值不改变原数组
package main
import "fmt"
func main() {
x := [3]int{1,2,3}
func(arr [3]int) {
arr[0] = 7
fmt.Println(arr) //prints [7 2 3]
}(x)
fmt.Println(x) //prints [1 2 3] (not ok if you need [7 2 3])
}
# 通过指针方式传递数组,变更原数组
package main
import "fmt"
func main() {
x := [3]int{1,2,3}
func(arr *[3]int) {
(*arr)[0] = 7
fmt.Println(arr) //prints &[7 2 3]
}(&x)
fmt.Println(x) //prints [7 2 3]
}
# slice作为参数,虽然也是值拷贝,但仍可变更引用的原始数据
package main
import "fmt"
func main() {
x := []int{1,2,3}
func(arr []int) {
arr[0] = 7
fmt.Println(arr) //prints [7 2 3]
}(x)
fmt.Println(x) //prints [7 2 3]
}
#错误
package main
func main() {
var x string = nil //错误
if x == nil { //错误
x = "default"
}
}
#正确
package main
func main() {
var x string //字符串的零值为""
if x == "" {
x = "default"
}
}
package main
func main() {
m := make(map[string]int,99)
cap(m) //错误,cap()函数返回的是“数组切片”分配的空间大小
}
#错误
package main
func main() {
var m map[string]int
m["one"] = 1 //空map不可以赋值
}
#正确
package main
func main() {
var s []int
s = append(s,1) //空slice可赋值
}
#错误
package main
func main() {
var x = nil //error
_ = x
}
#正确
package main
func main() {
var x interface{} = nil
_ = x
}
package main
import "fmt"
func main() {
x := 1
fmt.Println(x) //prints 1
{
fmt.Println(x) //prints 1
x := 2
fmt.Println(x) //prints 2
}
fmt.Println(x) //prints 1 (bad if you need 2)
}
#错误
package main
import (
"fmt"
)
type info struct {
result int
}
func work() (int,error) {
return 13,nil
}
func main() {
var data info
data.result, err := work() //错误
fmt.Printf("info: %+v\n",data)
}
#正确
package main
import (
"fmt"
)
type info struct {
result int
}
func work() (int,error) {
return 13,nil
}
func main() {
var data info
var err error
data.result, err = work() //正确
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("info: %+v\n",data) //prints: info: {result:13}
}
#错误
package main
func main() {
one := 0
one := 1 //错误,重复声明
}
#正确
package main
func main() {
one := 0
one, two := 1,2
one,two = two,one
}
#错误
package main
myvar := 1 //错误,短变量只能声明成员变量
func main() {
}
#正确
package main
var myvar = 1
func main() {
}
#错误
package main
import (
"fmt"
"log"
"time"
)
func main() {
}
#正确
package main
import (
_ "fmt"
"log"
"time"
)
var _ = log.Println
func main() {
_ = time.Now
}
# 错误
package main
var gvar int //正确,全局变量可以未使用
func main() {
var one int //错误,未使用的变量
two := 2 //错误,未使用的变量
var three int //错误,即使在下一行被赋值
three = 3
func(unused string) {
fmt.Println("Unused arg. No compile error")
}("what?")
}
#正确
package main
import "fmt"
func main() {
var one int
_ = one
two := 2
fmt.Println(two)
var three int
three = 3
one = three
var four int
four = four
}
# 错误
package main
import "fmt"
func main()
{ //错误,左大括号不能单独置于一行
fmt.Println("hello there!")
}
#正确
package main
import "fmt"
func main() {
fmt.Println("works!")
}
网友评论