這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
1. 手動實現append
package mainimport ("fmt")func Append(slice, data []byte) []byte {l := len(slice)total_len := len(slice) + len(data)if total_len >= cap(slice) {total_len = total_len * 2newslice := make([]byte, total_len)copy(newslice, slice)slice = newslice}for i, k := range data {slice[l+i] = k}return slice}func main() {slice := []byte{1, 2}data := []byte{4, 5, 6}sumslice := Append(slice, data)fmt.Println(sumslice)}
再看一遍這個程式的時候,我覺得直接迭代拷貝就可以了:
package mainimport("fmt")func SliceAppend(s1, s2 []byte)[]byte{s1_len:=len(s1)s2_len:=len(s2)total_len:=s1_len+s2_lens:=make([]byte, total_len)for k, v:=range s1{s[k]=v}for k, v:=range s2{s[s1_len+k]=v}return s}func main(){s1:=[]byte{1,2,3}s2:=[]byte{4,5,6}s:=SliceAppend(s1, s2)fmt.Println(s)}
2. 利用channel進行通訊
package mainimport "fmt"func main() {a := make(chan int)b := make(chan int)c := make(chan int)go func(a, b, c chan int) {fmt.Println("A wait")select {//case b <- 1://fmt.Println("This will never happen")case <-a:fmt.Println("A knows B exit")}fmt.Println("A exit")c <- 1}(a, b, c)go func(a, b chan int) {fmt.Println("B exit")close(b)a <- 1}(a, b)<-cfmt.Println("C exit")}
此例中a會等待b的退出,利用c來協調main與goroutine的運行。
3. golang的閉包函數
利用閉包修改傳回值:
package mainimport "fmt"func main() {x := 0exec(func() { x += 100 })fmt.Println(x)}func exec(callback func()) {callback()}
此例中,聲明一個fun()類型的參數,調用時進行具象化,傳遞給func()類型的參數,然後進行調用。
利用channel傳遞閉包函數:
package mainimport "fmt"import "math/rand"var c chan func()func main() {c = make(chan func())go loop()x := 0y := rand.Int() % 100exec(func() {for i := 0; i < y; i++ {x += 1}})fmt.Println("x= ", x)fmt.Println("y= ", y)}func loop() {for {select {case callback := <-c:callback()}}}func exec(callback func()) {c <- callback}
上例中利用閉包函數作為函數的參數,聲明一個func()類型的channel,並將此閉包函數通過channel進行傳遞,實現了函數同步與回調。
4. golang產生MD5值
任何字串產生的MD5結果長度都是固定的,利用golang產生一個字串的MD5值,然後與其它語言產生的MD5值進行比較:
package mainimport "fmt"import "crypto/md5"import "encoding/hex"import "bytes"func main() {h := md5.New()h.Write([]byte("你好,可以認識一下嗎?"))x := h.Sum(nil)y := make([]byte, 32)hex.Encode(y, x)fmt.Println(string(y))z := []byte("e2656fa718dbdb73b1c9b56b5ee2bcef")fmt.Println(bytes.Equal(y, z))}
5. 帶緩衝的channel
Go語言提供了兩種channel,帶緩衝區和不帶緩衝區的。不帶緩衝區的channel,發送和接收是同步的,必須接收端接收了訊息,發送端才解除阻塞。帶緩衝區的channel,在緩衝區滿之前,發送和接收是非同步,發送端的發送操作只保證把訊息放入緩衝區。channel可以關閉,關閉的目的是讓接收端知道不會再有訊息從這個channel進入,我們可能會用某個channel的關閉來表示某種狀態的終結。當關閉一個帶緩衝區的channel時,如果緩衝區中還有訊息,接收端在接收完所有訊息後,才會被告知channel已不可用。
package mainimport "fmt"import "runtime"func main() {runtime.GOMAXPROCS(4)bufferedChannel := make(chan int, 10)wait := make(chan int)for i := 0; i < 10; i++ {bufferedChannel <- i}close(bufferedChannel)go func() {for {if j, ok := <-bufferedChannel; ok {fmt.Println(j)} else {break}}wait <- 1}()<-wait}
結果是所有的值還是按順序列印,與“在緩衝區滿之前發送和接收是非同步”不太相符。
6. golang的指標運算
Go語言的文法上是不支援指標運算的,所有指標都在可控的一個範圍內使用,沒有C語言的*void然後隨意轉換指標類型這樣的東西。最近在思考Go如何操作共用記憶體,共用記憶體就需要把指標轉成不同類型或者對指標進行運算再擷取資料。對Go語言內建的unsafe模組做了一個實驗,發現通過unsafe模組,Go語言一樣可以做指標運算,只是比C的方式繁瑣一些,但是理解上是一樣的。
package mainimport "fmt"import "unsafe"type Data struct {Col1 byteCol2 intCol3 stringCol4 int}func main() {var v Datafmt.Println(unsafe.Sizeof(v))fmt.Println("----")fmt.Println(unsafe.Alignof(v.Col1))fmt.Println(unsafe.Alignof(v.Col2))fmt.Println(unsafe.Alignof(v.Col3))fmt.Println(unsafe.Alignof(v.Col4))fmt.Println("----")fmt.Println(unsafe.Offsetof(v.Col1))fmt.Println(unsafe.Offsetof(v.Col2))fmt.Println(unsafe.Offsetof(v.Col3))fmt.Println(unsafe.Offsetof(v.Col4))fmt.Println("----")v.Col1 = 98v.Col2 = 77v.Col3 = "1234567890abcdef"v.Col4 = 23fmt.Println(unsafe.Sizeof(v))fmt.Println("----")x := unsafe.Pointer(&v)fmt.Println(*(*byte)(x))fmt.Println(*(*int)(unsafe.Pointer(uintptr(x) + unsafe.Offsetof(v.Col2))))fmt.Println(*(*string)(unsafe.Pointer(uintptr(x) + unsafe.Offsetof(v.Col3))))fmt.Println(*(*int)(unsafe.Pointer(uintptr(x) + unsafe.Offsetof(v.Col4))))}
unsafe模組的文檔中提到幾條轉換規則,理解了以後就很容易做指標運算了:
- A pointer value of any type can be converted to a Pointer.
- A Pointer can be converted to a pointer value of any type.
- A uintptr can be converted to a Pointer.
- A Pointer can be converted to a uintptr
對於unsafe模組還需要看一下。
7. golang關閉channel
在一些情境下需要同時讓多個goroutine一起退出,最低限度需要幾個channel來做這件事情呢?下面是一則關閉頻道的實驗,實驗中運行兩個goruntine阻塞在channel c的訊息等待,然後在主函數中關閉channel c,從實驗結果可以看出兩個goruntine都正確的從阻塞的位置繼續運行下去。所以結論是,只需要通過關閉一個公用的channel就可以讓多個goruntine一起退出。
package mainimport "fmt"func main(){ a:=make(chan int) b:=make(chan int) c:=make(chan int) go func(){ <-c fmt.Println("A") a<-1 } go func(){ <-c fmt.Println("B") b<-1 } close(c) <-a <-b fmt.Println("End.")}
8. 實現自訂的ReadFull函數
package mainimport (//"errors""fmt""io")type UpString stringfunc (us UpString) Read(p []byte) (n int, e error) {i, lus, lp := 0, len(us), len(p)fmt.Println(lus, lp)for ; i < lus && i < lp; i++ {if us[i] >= 'a' && us[i] <= 'z' {p[i] = us[i] + 'A' - 'a'} else {p[i] = us[i]}}switch i {case lus:return lus, nilcase lp:return lp, io.EOFdefault:return i, io.EOF}}func main() {us := UpString("asdfASDFQWEV234VASDswf$%#@SDAFasdf")p := make([]byte, 68)n, err := io.ReadFull(us, p)fmt.Printf("%s\n", p)fmt.Println(n, err)}
將讀取緩衝設定為字串的2倍長度,會讀取兩次字串,直到讀滿為止。
9. golang介面與多態
package mainimport ("fmt""math")//---------- 接 口 --------//type shape interface {area() float64 //計算面積perimeter() float64 //計算周長}//--------- 長方形 ----------//type rect struct {width, height float64}func (r *rect) area() float64 { //面積return r.width * r.height}func (r *rect) perimeter() float64 { //周長return 2*(r.width + r.height)}//----------- 圓 形 ----------//type circle struct {radius float64}func (c *circle) area() float64 { //面積return math.Pi * c.radius * c.radius}func (c *circle) perimeter() float64 { //周長return 2 * math.Pi * c.radius}// ----------- 介面的使用 -----------//func interface_test() {r := rect {width:2.9, height:4.8}c := circle {radius:4.3}s := []shape{&r, &c} //通過指標實現for _, sh := range s {fmt.Println(sh)fmt.Println(sh.area())fmt.Println(sh.perimeter())}}func main(){interface_test()}
10. defer、panic樣本
package mainimport ("fmt")func g(i int) {if i>1 {fmt.Println("Panic!")panic(fmt.Sprintf("%v %s", i, "panic."))}}func f() {defer func() {if r := recover(); r != nil {fmt.Println("Recovered in f", r)}}()for i := 0; i < 4; i++ {fmt.Println("Calling g with ", i)g(i)fmt.Println("Returned normally from g.")}}func main() {f()fmt.Println("Returned normally from f.")}
可以人為的引入panic,然後在defer中利用recover來處理panic資訊。從以上程式可以看出,panic發生後,原函數停止向下執行,利用recover可以處理panic產生的資訊;處理完後繼續執行main()函數中下面的內容。