大數據開發-憑什麼Go語言可以殺出 一條血路

2018年,初識Go,用它寫過一個區塊鏈項目,不過也僅僅止步於此,後續工做都是Java和Scala居多,不過Go發展算是較晚的語言,以其簡潔,快等特色很快被大衆所熟知,一直認爲它是一門很是優秀的語言,後續的工做場景也使用較多Go,重拾起來也不算太難。從值不值得深刻學習Go方面講,其如今在高併發,雲場景,遊戲服務端等愈來愈被各大廠所使用,好比字節,B站,脈脈等,另外Go後面也有老爸-Google撐着,因此知名度愈來愈高,語言的最終目的不就是爲了簡潔嗎,而Go作到了這一點,從我的角度講,Go還沒Java那麼卷,因此競爭力越早越有優點。html

爲何是Go - Go的特色

Go提供了幾種基本但非必需的類型,好比切片,接口和通道。程序員

Go簡單不是它的主要賣點,作爲一門靜態語言,Go卻和不少動態腳本語言同樣得靈活是Go的主要賣點,節省內存、程序啓動快和代碼執行速度快合在一起是Go的另外一個主要賣點,Go是一門編譯型的和靜態的編程語言。Go誕生於谷歌研究院web

  • 內置併發編程支持:算法

    • 使用協程(goroutine)作爲基本的計算單元。輕鬆地建立協程。編程

    • 使用通道(channel)來實現協程間的同步和通訊。安全

  • 內置了映射(map)和切片(slice)類型。微信

  • 支持多態(polymorphism)。網絡

  • 使用接口(interface)來實現裝盒(value boxing)和反射(reflection)。閉包

  • 支持指針。架構

  • 支持函數閉包(closure)。

  • 支持方法。

  • 支持延遲函數調用(defer)。

  • 支持類型內嵌(type embedding)。

  • 支持類型推斷(type deduction or type inference)。

  • 內存安全。

  • 自動垃圾回收。

  • 良好的代碼跨平臺性。

編譯時間的長短是開發愉悅度的一個重要因素。編譯時間短是不少程序員喜歡Go的一個緣由

運行第一個go程序

## 運行go程序
go run 

## 打包go程序,生成可執行文件
go build 

## 來安裝一個第三方Go程序的最新版本,(至GOBIIN目錄) 在Go官方工具鏈1.16版本以前,對應的命令是go get -u example.com/program(如今已經被廢棄而再也不推薦被使用了
go install 

## 檢查可能的代碼邏輯錯誤
go vet

## 獲取網絡的依賴包,用拉添加、升級、降級或者刪除單個依賴,不如go mod tidy經常使用?
go get -u

## 生成go.mod 文件,依賴到的模塊
go mod init Demo.go

## 掃描當前項目中的全部代碼來添加未被記錄的依賴至go.mod文件或從go.mod文件中刪除再也不被使用的依賴
go mod tidy

## 格式化源文件代碼
go fmt Demo.go 

##  運行單元和基準測試用例
go test

##  查看Go代碼庫包的文檔
go doc 

## 運行go help aSubCommand來查看一個子命令aSubCommand的幫助信息 
go help 

GOROOT和GOPATH

GOROOT是Go語言環境的安裝路徑,在安裝開發環境時已經肯定 GOPATH是當前項目工程的開發路徑,GOPATH能夠有多個,每一個GOPATH下的通常有三個包,pkg、src和bin,src用於存放項目工程的源代碼文件,pkg文件夾下的文件在編譯時自動生成,bin目錄下生成*.exe的可執行文件。PS:每個GOPATH下均可以有pkg、src、bin三個文件夾,當設置多個GOPATH時,當前GOPATH的src源文件編譯結果和生成的可執行文件會存儲在最近路徑的GOPATH的pkg和bin文件夾下,即當前GOPATH下,開發時在src目錄下新建目錄並創建源代碼文件,目錄名稱和源文件名稱能夠不一樣,源文件內第一行代碼package pkgName中的pkgName也能夠和源文件所在文件夾名稱不一樣。可是,若是此包須要在其餘包中使用,編譯器會報錯,建議package 後的名稱和文件所在文件夾的名稱相同。通常只有main函數所在的源文件下才會出現所在包和「package 包名」聲明的包名不一樣的狀況

最終import 的包須要時package中寫的而不是目錄名,不然會報錯

Go的25個關鍵字

break     default      func    interface  select
case      defer        go      map        struct
chan      else         goto    package    switch
const     fallthrough  if      range      type
continue  for          import  return     var
  • constfuncimportpackagetypevar用來聲明各類代碼元素。

  • chaninterfacemapstruct用作 一些組合類型的字面表示中。

  • breakcasecontinuedefaultelsefallthroughforgotoifrangereturnselectswitch用在流程控制語句中。詳見基本流程控制語法。

  • defergo也能夠看做是流程控制關鍵字, 但它們有一些特殊的做用。詳見協程和延遲函數調用。

ps: uintptr、int以及uint類型的值的尺寸依賴於具體編譯器實現。一般地,在64位的架構上,int和uint類型的值是64位的;在32位的架構上,它們是32位的。編譯器必須保證uintptr類型的值的尺寸可以存下任意一個內存地址

常量自動補全和iota

在一個包含多個常量描述的常量聲明中,除了第一個常量描述,其它後續的常量描述均可以只有標識符部分。Go編譯器將經過照抄前面最緊挨的一個完整的常量描述來自動補全不完整的常量描述。好比,在編譯階段,編譯器會將下面的代碼

const (
  X float32 = 3.14
  Y                // 這裏必須只有一個標識符
  Z                // 這裏必須只有一個標識符

  A, B = "Go""language"
  C, _
  // 上一行中的空標識符是必需的(若是
  // 上一行是一個不完整的常量描述)。
)

自動補全爲

const (
  X float32 = 3.14
  Y float32 = 3.14
  Z float32 = 3.14

  A, B = "Go""language"
  C, _ = "Go""language"
)

iota是Go中預聲明(內置)的一個特殊的有名常量。iota被預聲明爲0,可是它的值在編譯階段並不是恆定。當此預聲明的iota出如今一個常量聲明中的時候,它的值在第n個常量描述中的值爲n(從0開始)。因此iota只對含有多個常量描述的常量聲明有意義。

iota和常量描述自動補全相結合有的時候可以給Go編程帶來很大便利。好比,下面是一個使用了這兩個特性的例子

const (
  Failed = iota - 1 // == -1
  Unknown           // == 0
  Succeeded         // == 1
)

const (
  Readable = 1 << iota // == 1
  Writable             // == 2
  Executable           // == 4
)

能夠看出,iota能夠在狀態值常量的應用上很方便,可是注意是在編譯時候使用哦

變量聲明方式

go語言中主要有下面兩種聲明方式

標準變量聲明方式

var (
  lang, bornYear, compiled     = "Go"2007true
  announceAt, releaseAt    int = 20092012
  createdBy, website       string
)

注意,Go聲明的局部變量要被有效使用一次,包變量沒有限制,另外不支持其餘語言所支持的連等賦值,以下圖:

var a, b int
a = b = 123 // 語法錯誤

短變量聲明方式

package main

func main() {
  // 變量lang和year都爲新聲明的變量。
  lang, year := "Go language"2007

  // 這裏,只有變量createdBy是新聲明的變量。
  // 變量year已經在上面聲明過了,因此這裏僅僅
  // 改變了它的值,或者說它被從新聲明瞭。
  year, createdBy := 2009"Google Research"

  // 這是一個純賦值語句。
  lang, year = "Go"2012

  print(lang, "由", createdBy, "發明")
  print("併發佈於", year, "年。")
  println()
}

:= 這種方式聲明的還有個特色就是,短聲明語句中必須至少有一個新聲明的變量,能夠支持前面部分聲明過的變量再繼續聲明賦值

函數聲明和調用

分別爲 func 函數名 參數 返回值 函數體(包含返回值或者不包含)

其餘都好理解,有一點須要注意在Go中和其餘語言不一樣之處,就是返回值若是匿名聲明,那麼return 的時候須要明確返回變量或者某個值,若是是非匿名聲明,那麼就能夠只寫return 這個和其餘語言仍是有一點區別的,若是函數沒有返回值 ,那麼return就不用寫,Go不支持輸入參數默認值。每一個返回結果的默認值是它的類型的零值。好比,下面的函數在被調用時將打印出(和返回)0 false

func f() (x int, y bool) {
  println(x, y) // 0 false
  return
}

// 我的不是很懂這個,理解不了 

Go流程控制語法

三種基本的流程控制代碼塊:

  • if-else條件分支代碼塊;

  • for循環代碼塊;

  • switch-case多條件分支代碼塊。

還有幾種和特定種類的類型相關的流程控制代碼塊:

  • 容器類型相關的for-range循環代碼塊。

  • 接口類型相關的type-switch多條件分支代碼塊。

  • 通道類型相關的select-case多分支代碼塊。

Go併發同步 - sync.WaitGroup

併發編程的一大任務就是要調度不一樣計算,控制它們對資源的訪問時段,以使數據競爭的狀況不會發生。此任務常稱爲併發同步(或者數據同步)。Go支持幾種併發同步技術,先學習最簡單的一種,sync標準庫中的WaitGroup來同步主協程和建立的協程

WaitGroup類型有三個方法(特殊的函數,將在之後的文章中詳解):AddDoneWait。此類型將在後面的某篇文章中詳細解釋,目前咱們能夠簡單地認爲:

  • Add方法用來註冊新的須要完成的任務數。

  • Done方法用來通知某個任務已經完成了。

  • 一個Wait方法調用將阻塞(等待)到全部任務都已經完成以後才繼續執行其後的語句

package main

import (
  "log"
  "math/rand"
  "time"
  "sync"
)

var wg sync.WaitGroup

func SayGreetings(greeting string, times int) {
  for i := 0; i < times; i++ {
    log.Println(greeting)
    d := time.Second * time.Duration(rand.Intn(5)) / 2
    time.Sleep(d)
  }
  wg.Done() // 通知當前任務已經完成。
}

func main() {
  rand.Seed(time.Now().UnixNano())
  log.SetFlags(0)
  wg.Add(2// 註冊兩個新任務。
  go SayGreetings("hi!"10)
  go SayGreetings("hello!"10)
  wg.Wait() // 阻塞在這裏,直到全部任務都已完成。
}

能夠看到一個活動中的協程能夠處於兩個狀態:運行狀態和阻塞狀態。一個協程能夠在這兩個狀態之間切換


協程的調度

編譯器採納了一種被稱爲M-P-G模型的算法來實現協程調度。其中,M表示系統線程,P表示邏輯處理器(並不是上述的邏輯CPU),G表示協程。大多數的調度工做是經過邏輯處理器(P)來完成的。邏輯處理器像一個監工同樣經過將不一樣的處於運行狀態協程(G)交給不一樣的系統線程(M)來執行。一個協程在同一時刻只能在一個系統線程中執行。一個執行中的協程運行片刻後將自發地脫離讓出一個系統線程,從而使得其它處於等待子狀態的協程獲得執行機會,以下圖,P能夠理解爲總司令,G是程序寫好的邏輯協程,而M是具體的系統線程執行器

總結

有過語言基礎的小夥伴入門Go不算很難 ,一週上手項目,一個月學會常見輪子,半年能夠從0-1手寫項目,一年能夠在簡歷上寫熟悉Go開發,下面的是學習主要參考文檔,強烈建議多看幾遍

Go語言101 :https://gfw.go101.org/article/101-about.html



本文分享自微信公衆號 - 數據咖(bigbatacoffee)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索