Go語言初探

Go語言基礎

規則

大寫字母開頭變量爲共有變量,小寫字母開頭爲私有變量(函數類似)

Iota

iota枚舉(默認開始值爲0,const中每增加一行,值加1)

slice

引用類型(類似於動態數組),相比於array,它沒有長度限制

make & new

  • make返回初始化後的值(非零),只能被用於slice、map、channel
  • new返回指針,一般很少被使用

goto(無條件跳轉,配合標籤使用,無條件跳轉至標籤,可用於跳出循環)

由於可隨意跳轉,在龐大的系統中不利於debug,所以在coding中會通過結構化程序設計來規避這類問題,那麼,在go語言設計者爲何會繼續沿用goto呢?

  • 最開始提出goto有害論主要是當時的背景導致的,最開始使用的是非結構化的過程語言,例如BASIC和各種彙編語言
  • 可讀性強,可以書寫出乾淨的退出功能(將所有退出方式統一編輯,利用goto進行跳轉)
  • 低級性能改進,goto語句執行的非常快

defer(延遲語句,函數執行到最後會按照逆序執行defer語句,可用於處理資源泄漏問題)

  • 在函數中使用時,採用先進後出模式

panic & recover(go沒有異常機制,使用這兩個內置函數來代替異常機制)

  • 調用panic中斷函數執行(但defer語句仍然會被執行),退出執行
  • 利用recover + defer可以捕獲panic的輸入值,並恢復正常執行 
package main

import "fmt"

func test_goto() {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()
	for sum := 0; sum < 15; sum++ {
		if sum == 12 {
			panic("12 is error")
		}
		fmt.Println(sum)
	}
}

func main() {
	test_goto()
	fmt.Println("over!")
}


輸出結果爲:
0
1
2
3
4
5
6
7
8
9
10
11
12 is error
over!

使用指針作爲receiver

如果不使用指針作爲receiver,那麼傳遞的是參數的copy,如果修改值,只會作用在copy上

interface

GoLang本身是不支持泛型(通用類型)編程的,這也是它被詬病最多的地方,爲了彌補這一缺陷,可以通過interface來實現泛型編程(PS:go只是暫時不支持,因爲這會增加設計的複雜度,以後可能會支持)

反射

Go利用reflect實現反射(即檢查程序在運行時的狀態),要去反射是一個類型的值 (這些值都實現了空 interface),首先需要把它轉化成 reflect 對象 (reflect.Type 或者 reflect.Value,根據不同的情況調用不同的函數)。

要反射的字段必須是可讀寫的字段,否則會報錯。

併發

goroutine

goroutine 是 Go 並行設計的核心。goroutine 說到底其實就是協程,但是它比線程更小,十幾個 goroutine 可能體現在底層就是五六個線程,Go 語言內部幫你實現了這些 goroutine 之間的內存共享。執行 goroutine 只需極少的棧內存 (大概是 4~5 KB),當然會根據相應的數據伸縮。也正因爲如此,可同時運行成千上萬個併發任務。goroutine 比 thread 更易用、更高效、更輕便。

設計上我們要遵循:不要通過共享來通信,而要通過通信來共享。

但在 Go 1.5 以前調度器僅使用單線程,也就是說只實現了併發。想要發揮多核處理器的並行,需要在我們的程序中顯式調用 runtime.GOMAXPROCS (n) 告訴調度器同時使用多個線程。GOMAXPROCS 設置了同時運行邏輯代碼的系統線程的最大數量,並返回之前的設置。如果 n < 1,不會改變當前設置。

channel

channel類型的值可以發送接收值,注意,必須使用 make 創建 channel。默認情況下,channel 接收和發送數據都是阻塞的,除非另一端已經準備好,這樣就使得 Goroutines 同步變的更加的簡單,而不需要顯式的 lock。所謂阻塞,也就是如果讀取(value := <-ch)它將會被阻塞,直到有數據接收。其次,任何發送(ch<-5)將會被阻塞,直到數據被讀出。無緩衝 channel 是在多個 goroutine 之間同步很棒的工具。

 Go 也允許指定 channel 的緩衝大小,很簡單,就是 channel 可以存儲多少元素。ch:= make (chan bool, 4),創建了可以存儲 4 個元素的 bool 型 channel。在這個 channel 中,前 4 個元素可以無阻塞的寫入。當寫入第 5 個元素時,代碼將會阻塞,直到其他 goroutine 從 channel 中讀取一些元素,騰出空間。

select

select可以監聽channel上的數據流動。select默認是阻塞的,只有當監聽的 channel 中有發送或接收可以進行時纔會運行,當多個 channel 都準備好的時候,select 是隨機的選擇一個執行的。

select {
case i := <-c:
    // use i
default:
    // 當 c 阻塞的時候執行這裏
}

select其實就是類似 switch 的功能,default 就是當監聽的 channel 都沒有準備好的時候,默認執行的(select 不再阻塞等待 channel)。

我們可以使用select來設置超時以避免goroutine阻塞導致的整個程序阻塞。

func main() {
    c := make(chan int)
    o := make(chan bool)
    go func() {
        for {
            select {
                case v := <- c:
                    println(v)
                case <- time.After(5 * time.Second):
                    println("timeout")
                    o <- true
                    break
            }
        }
    }()
    <- o
}

 

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