Go基礎(chǔ)

變量

變量聲明:var name type

聲明變量時若賦了初始值外驱,可以省略類型。

可以同時聲明多個相同類型的變量俊性,如 var a,b = 100,3

定義多個不同類型的變量可如下定義

var (
    name1 = initialvalue1
    name2 = initialvalue2
)

對于局部變量略步,可以使用簡潔的語法聲明變量,如 name:=initialvalue

注意:局部變量聲明之后若不使用會報錯定页,且:=左邊的變量至少要有一個是之前未聲明的

當(dāng)使用:=聲明多個變量時趟薄,多個變量的類型可以不同。

類型

  • bool
  • Numeric Types
    • int8, int16, int32, int64, int
    • uint8, uint16, uint32, uint64, uint
    • float32, float64
    • complex64, complex128
    • byte
    • rune
  • string

數(shù)值類型進行計算時典徊,不同數(shù)值類型不會自動轉(zhuǎn)換杭煎,需要強轉(zhuǎn)

函數(shù)

函數(shù)聲明如下:

func functionname(parametername type) returntype {  
 //function body
}

當(dāng)函數(shù)返回多個值時,可以使用 _ 接收不需要的返回值卒落。

導(dǎo)入包

import (
    packagename
)

每個包都可以有一個init函數(shù)羡铲,用來執(zhí)行初始化,當(dāng)包被導(dǎo)入時該函數(shù)會被執(zhí)行儡毕。

對于導(dǎo)入的包也切,若之后不使用包的內(nèi)容扑媚,編譯時會報錯。但是某種情況下又希望執(zhí)行該包的init函數(shù)雷恃,此時可以如下導(dǎo)包

import (  
    "geometry/rectangle" 
)

結(jié)構(gòu)體

對于匿名結(jié)構(gòu)體疆股,他的類型就是他的”鍵“。

當(dāng)結(jié)構(gòu)體中包含一個匿名結(jié)構(gòu)體倒槐,該匿名結(jié)構(gòu)體的字段對于外面的結(jié)構(gòu)體來說稱為遞進字段旬痹,可以直接訪問。

package main

import (  
    "fmt"
)

type Address struct {  
    city  string
    state string
}
type Person struct {  
    name string
    age  int
    Address
}

func main() {  
    p := Person{
        name: "Naveen",
        age:  50,
        Address: Address{
            city:  "Chicago",
            state: "Illinois",
        },
    }

    fmt.Println("Name:", p.name)
    fmt.Println("Age:", p.age)
    fmt.Println("City:", p.city)   //city is promoted field
    fmt.Println("State:", p.state) //state is promoted field
}

類似的讨越,對于屬于匿名結(jié)構(gòu)體的方法两残,外部的結(jié)構(gòu)體也可以直接調(diào)用,就像是自己的方法把跨。

接口

下面的例子說明了聲明方法的不同方式帶來的差異

package main

import "fmt"

type Describer interface {  
    Describe()
}
type Person struct {  
    name string
    age  int
}

func (p Person) Describe() { //implemented using value receiver  
    fmt.Printf("%s is %d years old\n", p.name, p.age)
}

type Address struct {  
    state   string
    country string
}

func (a *Address) Describe() { //implemented using pointer receiver  
    fmt.Printf("State %s Country %s", a.state, a.country)
}

func main() {  
    var d1 Describer
    p1 := Person{"Sam", 25}
    d1 = p1
    d1.Describe()
    p2 := Person{"James", 32}
    d1 = &p2
    d1.Describe()

    var d2 Describer
    a := Address{"Washington", "USA"}

    /* compilation error if the following line is
       uncommented
       cannot use a (type Address) as type Describer
       in assignment: Address does not implement
       Describer (Describe method has pointer
       receiver)
    */
    //d2 = a

    d2 = &a //This works since Describer interface
    //is implemented by Address pointer in line 22
    d2.Describe()

}

接口繼承

package main

import (  
    "fmt"
)

type SalaryCalculator interface {  
    DisplaySalary()
}

type LeaveCalculator interface {  
    CalculateLeavesLeft() int
}

type EmployeeOperations interface {  
    SalaryCalculator
    LeaveCalculator
}

type Employee struct {  
    firstName string
    lastName string
    basicPay int
    pf int
    totalLeaves int
    leavesTaken int
}

func (e Employee) DisplaySalary() {  
    fmt.Printf("%s %s has salary $%d", e.firstName, e.lastName, (e.basicPay + e.pf))
}

func (e Employee) CalculateLeavesLeft() int {  
    return e.totalLeaves - e.leavesTaken
}

func main() {  
    e := Employee {
        firstName: "Naveen",
        lastName: "Ramanathan",
        basicPay: 5000,
        pf: 200,
        totalLeaves: 30,
        leavesTaken: 5,
    }
    var empOp EmployeeOperations = e
    empOp.DisplaySalary()
    fmt.Println("\nLeaves left =", empOp.CalculateLeavesLeft())
}

并發(fā)

Goroutines

Goroutines可以認為是輕量級的線程人弓,用于并發(fā)的運行函數(shù)或方法。

Goroutines使用頻道(channels)進行通信节猿,channels就類似于管道(pipe)票从。

使用方法:在函數(shù)前使用go關(guān)鍵字

package main

import (  
    "fmt"
)

func hello() {  
    fmt.Println("Hello world goroutine")
}
func main() {  
    go hello()
    time.Sleep(1 * time.Second)
    fmt.Println("main function")
}

上面的例子中,會起一個goroutine來運行hello函數(shù)滨嘱,原來的main函數(shù)則在main goroutine中運行峰鄙。

Channels

聲明chan T 是類型T的一個channel,用于傳輸類型為T的數(shù)據(jù)

可以使用make進行聲明太雨,如 a := make(chan int)

接收和發(fā)送數(shù)據(jù):

data := <- a // read from channel a  
a <- data // write to channel a  

接收和發(fā)送數(shù)據(jù)默認是阻塞的吟榴,即當(dāng)發(fā)送數(shù)據(jù)時控制過程會阻塞,直到有g(shù)oroutine接收了數(shù)據(jù)囊扳。

demo:

package main

import (  
    "fmt"
)

func hello(done chan bool) {  
    fmt.Println("Hello world goroutine")
    done <- true
}
func main() {  
    done := make(chan bool)
    go hello(done)
    <-done
    fmt.Println("main function")
}

在循環(huán)中使用channel時需要使用close函數(shù)來關(guān)閉channel吩翻,避免死鎖。

package main

import (  
    "fmt"
)

func producer(chnl chan int) {  
    for i := 0; i < 10; i++ {
        chnl <- i
    }
    close(chnl)
}
func main() {  
    ch := make(chan int)
    go producer(ch)
    for {
        v, ok := <-ch
        if ok == false {
            break
        }
        fmt.Println("Received ", v, ok)
    }
}
//和上面相同 不過使用的是range
func main() {  
    ch := make(chan int)
    go producer(ch)
    for v := range ch {
        fmt.Println("Received ",v)
    }
}

buffered channels

帶有buffer的channel聲明:ch := make(chan type, capacity)

帶buffer的channel只有在buffer滿了或為空(向channel發(fā)送數(shù)據(jù)/從channel接收數(shù)據(jù))時才會阻塞锥咸。普通的channel可以認為是buffer capacity為0的buffered channel狭瞎。

WaitGroup

WaitGroup類似于一道"屏障",只有當(dāng)WaitGroup里面的goroutine全部執(zhí)行完畢搏予,才能繼續(xù)往下走熊锭。

package main

import (  
    "fmt"
    "sync"
    "time"
)

func process(i int, wg *sync.WaitGroup) {  
    fmt.Println("started Goroutine ", i)
    time.Sleep(2 * time.Second)
    fmt.Printf("Goroutine %d ended\n", i)
    wg.Done()
}

func main() {  
    no := 3
    var wg sync.WaitGroup
    for i := 0; i < no; i++ {
        wg.Add(1)
        go process(i, &wg)
    }
    wg.Wait()
    fmt.Println("All go routines finished executing")
}

在上面的例子中,函數(shù)的waitgroup參數(shù)必須是個指針雪侥,否則每個函數(shù)拿到的只是waitgroup的一份copy碗殷,函數(shù)(此處也是goroutine)執(zhí)行完后,mainroutine的wg也沒有反應(yīng)速缨,從而造成死鎖锌妻。

select

select會阻塞,直到select聲明模塊中至少有一個channel可用旬牲。對于同時有多個可用的channel仿粹,select會隨機選擇其他一個channel進行操作搁吓。

語法類似于switch,如下

package main

import (  
    "fmt"
    "time"
)

func server1(ch chan string) {  
    time.Sleep(6 * time.Second)
    ch <- "from server1"
}
func server2(ch chan string) {  
    time.Sleep(3 * time.Second)
    ch <- "from server2"

}
func main() {  
    output1 := make(chan string)
    output2 := make(chan string)
    go server1(output1)
    go server2(output2)
    select {
    case s1 := <-output1:
        fmt.Println(s1)
    case s2 := <-output2:
        fmt.Println(s2)
    }
}

mutex

mutex就是互斥量的意思吭历,用于保證某一部分的代碼同時只能有一個goroutine運行擎浴,其他goroutine會阻塞,直到上一個goroutine釋放了鎖毒涧。

package main  
import (  
    "fmt"
    "sync"
    )
var x  = 0  
func increment(wg *sync.WaitGroup, m *sync.Mutex) {  
    m.Lock()
    x = x + 1
    m.Unlock()
    wg.Done()   
}
func main() {  
    var w sync.WaitGroup
    var m sync.Mutex
    for i := 0; i < 1000; i++ {
        w.Add(1)        
        go increment(&w, &m)
    }
    w.Wait()
    fmt.Println("final value of x", x)
}

也可以使用channel完成上述例子的功能,因為channel本身就是阻塞的贝室,代碼如下

package main  
import (  
    "fmt"
    "sync"
    )
var x  = 0  
func increment(wg *sync.WaitGroup, ch chan bool) {  
    ch <- true
    x = x + 1
    <- ch
    wg.Done()   
}
func main() {  
    var w sync.WaitGroup
    ch := make(chan bool, 1)
    for i := 0; i < 1000; i++ {
        w.Add(1)        
        go increment(&w, ch)
    }
    w.Wait()
    fmt.Println("final value of x", x)
}

mutex和channel的選擇

從上面可以看出mutex和channel可以完成同樣的事情契讲,那么mutex和channel該如何選擇呢?

通常來講滑频,channel用于在goroutine間傳遞數(shù)據(jù)捡偏,而mutex用于限制只有一個goroutine進入的代碼段(critical section)。

Defer

defer用于推遲函數(shù)或方法的執(zhí)行峡迷,在函數(shù)或方法返回前才調(diào)用defer語句银伟。下面的例子可以很好的理解的defer的使用。

package main

import (  
    "fmt"
)

func finished() {  
    fmt.Println("Finished finding largest")
}

func largest(nums []int) {  
    defer finished()    
    fmt.Println("Started finding largest")
    max := nums[0]
    for _, v := range nums {
        if v > max {
            max = v
        }
    }
    fmt.Println("Largest number in", nums, "is", max)
}

func main() {  
    nums := []int{78, 109, 2, 563, 300}
    largest(nums)
}

上面的輸出結(jié)果為:

Started finding largest
Largest number in [78 109 2 563 300] is 563
Finished finding largest

從上面可以看出defer的finished函數(shù)在largest函數(shù)里绘搞,直到largest返回時才執(zhí)行finished函數(shù)彤避,即finished函數(shù)是最后執(zhí)行的。

defer函數(shù)的參數(shù)計算

defer函數(shù)的參數(shù)在defer語句執(zhí)行時計算夯辖,而不是在實際函數(shù)調(diào)用完成時計算琉预,具體的可以看下面的例子。

package main

import (  
    "fmt"
)

func printA(a int) {  
    fmt.Println("value of a in deferred function", a)
}
func main() {  
    a := 5
    defer printA(a)
    a = 10
    fmt.Println("value of a before deferred function call", a)

}

輸出的結(jié)果為

value of a before deferred function call 10
value of a in deferred function 5

多個defer語句的執(zhí)行

當(dāng)一個函數(shù)里定義了多個defer語句蒿褂,defer的執(zhí)行順序為LIFO圆米,即后進先出。

package main

import (  
    "fmt"
)

func main() {  
    name := "Naveen"
    fmt.Printf("Original String: %s\n", string(name))
    fmt.Printf("Reversed String: ")
    for _, v := range []rune(name) {
        defer fmt.Printf("%c", v)
    }
}

結(jié)果為

Original String: Naveen
Reversed String: neevaN

遇到panic時啄栓,defer語句仍會執(zhí)行

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末娄帖,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子昙楚,更是在濱河造成了極大的恐慌近速,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件桂肌,死亡現(xiàn)場離奇詭異数焊,居然都是意外死亡,警方通過查閱死者的電腦和手機崎场,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門佩耳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谭跨,你說我怎么就攤上這事干厚±畹危” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵蛮瞄,是天一觀的道長所坯。 經(jīng)常有香客問我,道長挂捅,這世上最難降的妖魔是什么芹助? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮闲先,結(jié)果婚禮上状土,老公的妹妹穿的比我還像新娘。我一直安慰自己伺糠,他們只是感情好蒙谓,可當(dāng)我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著训桶,像睡著了一般累驮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上舵揭,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天谤专,我揣著相機與錄音,去河邊找鬼琉朽。 笑死毒租,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的箱叁。 我是一名探鬼主播墅垮,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼耕漱!你這毒婦竟也來了算色?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤螟够,失蹤者是張志新(化名)和其女友劉穎灾梦,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體妓笙,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡若河,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了寞宫。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片萧福。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖辈赋,靈堂內(nèi)的尸體忽然破棺而出鲫忍,到底是詐尸還是另有隱情膏燕,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布悟民,位于F島的核電站坝辫,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏射亏。R本人自食惡果不足惜近忙,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望智润。 院中可真熱鬧银锻,春花似錦、人聲如沸做鹰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽钾麸。三九已至,卻和暖如春炕桨,著一層夾襖步出監(jiān)牢的瞬間饭尝,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工献宫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留钥平,地道東北人。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓姊途,卻偏偏與公主長得像涉瘾,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子捷兰,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 基礎(chǔ) [TOC] 特性 Go 并發(fā)編程采用CSP模型不需要鎖立叛,不需要callback并發(fā)編程 vs 并行計算 安裝...
    蕪園樓主香獨秀閱讀 249評論 0 0
  • 函數(shù)的聲明 在 Go 語言中,函數(shù)聲明通用語法如下: 1.函數(shù)的聲明以關(guān)鍵詞 func開始 贡茅。 函數(shù)名和參數(shù)列表一...
    _羊羽_閱讀 1,722評論 1 4
  • 運算符 算數(shù)運算符 關(guān)系運算符 邏輯運算符 位運算符 賦值運算符 其他運算符ps:為防止發(fā)生混淆秘蛇,go語法規(guī)定,+...
    名字剛好七個字閱讀 409評論 1 0
  • CSP 要想理解 channel 要先知道 CSP 模型顶考。CSP 是 Communicating Sequenti...
    _羊羽_閱讀 1,601評論 0 0
  • Go語言并發(fā) Go 是并發(fā)式語言赁还,而不是并行式語言。 并發(fā)是指立即處理多個任務(wù)的能力驹沿。 Go 編程語言原生支持并發(fā)...
    kakarotto閱讀 1,892評論 0 7