大數據開發-Go-新手常遇問題

真正在工作中用Go的時間不久,所以也作爲新手,總結了一些常見的問題和坑

Go 中指針使用注意點

// 1.空指針反向引用不合法
package main
func main() {
    var p *int = nil
    *p = 0
}
// in Windows: stops only with: <exit code="-1073741819" msg="process crashed"/>
// runtime error: invalid memory address or nil pointer dereference

// 2.文字或者常量引用也不合法
const i = 5
ptr := &i //error: cannot take the address of i
ptr2 := &10 //error: cannot take the address of 10

Go語言常見內置函數

sort

// sort 包
import "sort"
sort.Strings(keys)

close 用於管道通信,select 用於通信的switch

type T int
func main() {
  c := make(chan T)
  close(c)
}

// select 用法
var c1, c2, c3 chan int
var i1, i2 int
select {
  case i1 = <-c1:
     fmt.Printf("received ", i1, " from c1\n")
  case c2 <- i2:
     fmt.Printf("sent ", i2, " to c2\n")
  case i3, ok := (<-c3):  // same as: i3, ok := <-c3
     if ok {
        fmt.Printf("received ", i3, " from c3\n")
     } else {
        fmt.Printf("c3 is closed\n")
     }
  default:
     fmt.Printf("no communication\n")
}    

len、cap

len 用於返回某個類型的長度或數量(字符串、數組、切片、map 和管道);

cap 是容量的意思,用於返回某個類型的最大容量(只能用於切片和 map)

new、make

new 和 make 均是用於分配內存:new 用於值類型和用戶定義的類型,如自定義結構,make 用於內置引用類型(切片、map 和管道)。

它們的用法就像是函數,但是將類型作爲參數:new (type)、make (type)。new (T) 分配類型 T 的零值並返回其地址,也就是指向類型 T 的指針。

它也可以被用於基本類型:v := new(int)。make (T) 返回類型 T 的初始化之後的值,因此它比 new 進行更多的工作,new () 是一個函數,不要忘記它的括號

copy、append

用於複製和連接切片

panic、recover

兩者均用於錯誤處理機制

print、println

底層打印函數,在部署環境中建議使用 fmt 包

complex、real、imag

操作複數,使用場景不多

Go不支持函數重載

Go 語言不支持這項特性的主要原因是函數重載需要進行多餘的類型匹配影響性能;沒有重載意味着只是一個簡單的函數調度。所以你需要給不同的函數使用不同的名字,我們通常會根據函數的特徵對函數進行命名

如果需要申明一個在外部定義的函數,你只需要給出函數名與函數簽名,不需要給出函數體:

func flushICache(begin, end uintptr) // implemented externally

函數也可以以申明的方式被使用,作爲一個函數類型,就像:

type binOp func(int, int) int

Go的map遍歷時候變量地址一直用的是同一個

最佳實踐:讀數據可以用key,value,寫數據用地址,如果要把地址賦值給另外的map,那麼需要用臨時變量

  kvMap := make(map[int]int)
  kvMap[0] = 100
  kvMap[1] = 101
  kvMap[2] = 102
  kvMap[3] = 103
  
  for k, v := range kvMap {
    println(k, &k, v, &v)
  }
 
// 0 0xc000049e50 100 0xc000049e48
// 1 0xc000049e50 101 0xc000049e48
// 2 0xc000049e50 102 0xc000049e48
// 3 0xc000049e50 103 0xc000049e48 

Go遍歷的key,value是值,而不是地址

// Version A:
items := make([]map[int]int, 5)
for i:= range items {
    items[i] = make(map[int]int, 1)
    items[i][1] = 2
}
fmt.Printf("Version A: Value of items: %v\n", items)

// Version B: NOT GOOD!
items2 := make([]map[int]int, 5)
for _, item := range items2 {
    item = make(map[int]int, 1) // item is only a copy of the slice element.
    item[1] = 2 // This 'item' will be lost on the next iteration.
}
fmt.Printf("Version B: Value of items: %v\n", items2)


應當像 A 版本那樣通過索引使用切片的 map 元素。在 B 版本中獲得的項只是 map 值的一個拷貝而已,所以真正的 map 元素沒有得到初始化

鎖和 sync 包

併發編程在大部分語言裏面都會有,用來解決多個線程對臨界資源的訪問,經典的做法是一次只能讓一個線程對共享變量進行操作。當變量被一個線程改變時 (臨界區),我們爲它上鎖,直到這個線程執行完成並解鎖後,其他線程才能訪問它,Go語言的這種鎖機制是通過sync包中的Mutex來實現訪問的,下面是一個例子,另外在sync包中還有一個讀寫鎖,RWMutex ,其寫互斥的方法與Mutex一樣,讀互斥採用下面的方法

mu sync.Mutex

func update(a int) {
  mu.Lock()
  a = xxx
  mu.Unlock()
}

mu2 sync.RWMutex
mu2.RLock()
mu2.RUnlock()

吳邪,小三爺,混跡於後臺,大數據,人工智能領域的小菜鳥。
更多請關注
file

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章