golang搭建項目教程

golang與Java的不同:go的特性體現在通道和高併發,可做中間件,雲計算,而java是業務性語言,搭建微服務架構。
項目下的vendor目錄
GOROOT/src
GOPATH/src
在這裏插入圖片描述
依賴包加到本地:

go get github.com/go-sql-driver/mysql

govendor:go get只能算下載器而已,govendor提供下載,更新,移除、添加等多種管理命令。

go get -u github.com/kardianos/govendor
cd go_project/src/hello_world
govendor init
govendor fetch github.com/go-sql-driver/mysql
go get github.com/garyburd/redigo/redis
go get -u github.com/confluentinc/confluent-kafka-go/kafka
git clone https://github.com/nsqio/nsq.git;cd nsq;gpm install

這樣我們就能在vendor下找到mysql,也能在項目中導入了。

協程是單線程下的併發,又稱微線程,纖程
協程是一種用戶態的輕量級線程,即協程是由用戶程序自己控制調度的
協程能夠實現在一條線程上的多個任務互相切換
爲了提高工作效率,用戶可以控制在一個任務中遇到io就切換
協程由於是在用戶態來完成上下文切換的,所以切換耗時只有區區100ns多一些,比進程切換要高30倍。單個協程需要的棧內存也足夠小,只需要2KB。
協程底層實現原理
  線程是操作系統的內核對象,多線程編程時,如果線程數過多,就會導致頻繁的上下文切換(是指CPU 從一個進程或線程切換到另一個進程或線程。
  ),這些 cpu 時間是一個額外的耗費。所以在一些高併發的網絡服務器編程中,使用一個線程服務一個 socket 連接是很不明智的。於是操作系統提供了基於事件模式的異步編程模型。用少量的線程來服務大量的網絡連接和I/O操作。但是採用異步和基於事件的編程模型,複雜化了程序代碼的編寫,非常容易出錯。因爲線程穿插,也提高排查錯誤的難度。

協程,是在應用層模擬的線程,他避免了上下文切換的額外耗費,兼顧了多線程的優點。簡化了高併發程序的複雜度。舉個例子,一個高併發的網絡服務器,每一個socket連接進來,服務器用一個協程來對他進行服務。代碼非常清晰。而且兼顧了性能。
他和線程的原理是一樣的,當 a線程 切換到 b線程 的時候,需要將 a線程 的相關執行進度壓入棧,然後將 b線程 的執行進度出棧,進入 b線程 的執行序列。協程只不過是在 應用層 實現這一點。但是,協程並不是由操作系統調度的,而且應用程序也沒有能力和權限執行 cpu 調度。怎麼解決這個問題?

答案是,協程是基於線程的。內部實現上,維護了一組數據結構和 n 個線程,真正的執行還是線程,協程執行的代碼被扔進一個待執行隊列中,由這 n 個線程從隊列中拉出來執行。這就解決了協程的執行問題。那麼協程是怎麼切換的呢?答案是:golang 對各種 io函數 進行了封裝,這些封裝的函數提供給應用程序使用,而其內部調用了操作系統的異步 io函數,當這些異步函數返回 busy 或 bloking 時,golang 利用這個時機將現有的執行序列壓棧,讓線程去拉另外一個協程的代碼來執行,基本原理就是這樣,利用並封裝了操作系統的異步函數。包括 linux 的 epoll、select 和 windows 的 iocp、event 等。

func Add(x, y int) {
    z := x + y
    fmt.Println(z)
}
 
func main() {
    for i:=0; i<10; i++ {
        go Add(i, i)
    }
}

執行上面的代碼,會發現屏幕什麼也沒打印出來,程序就退出了。
  對於上面的例子,main()函數啓動了10個goroutine,然後返回,這時程序就退出了,而被啓動的執行 Add() 的 goroutine 沒來得及執行。我們想要讓 main() 函數等待所有 goroutine 退出後再返回,但如何知道 goroutine 都退出了呢?這就引出了多個goroutine之間通信的問題。

func Count(ch chan int) {
    ch <- 1
    fmt.Println("Counting")
}
 
func main() {
 
    chs := make([] chan int, 10)
 
    for i:=0; i<10; i++ {
        chs[i] = make(chan int)
        go Count(chs[i])
    }
 
    for _, ch := range(chs) {
        <-ch
    }
}

在這個例子中,定義了一個包含10個channel的數組,並把數組中的每個channel分配給10個不同的goroutine。在每個goroutine完成後,向goroutine寫入一個數據,在這個channel被讀取前,這個操作是阻塞的。在所有的goroutine啓動完成後,依次從10個channel中讀取數據,在對應的channel寫入數據前,這個操作也是阻塞的。這樣,就用channel實現了類似鎖的功能,並保證了所有goroutine完成後main()才返回。
go實例

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