Golang併發程式設計優勢與核心goroutine及注意細節

OldBoy~發表於2018-09-16

Go語言為併發程式設計而內建的上層API基於CSP(communication sequential processes,順序通訊程式)模型。這就意味著顯式鎖都是可以避免的,比如資源競爭,比如多個程式同時獲取檔案資源需要修改,首先拿到資源的程式加上鎖,等修改完之後把鎖去掉,然後再給下一個程式來進行修改,只有這樣才不會出現資料不一致。但是go語言不是通過鎖的方式,是通過通訊的方式,安全的通道傳送和接收資料以實現同步,這就大大的簡化了併發程式設計的編寫。

一般情況下,一個普通的計算機跑十幾二十幾個執行緒就有點負載過大,但是同樣這臺機器卻可以讓成千甚至過萬哥goroutine進行資源競爭

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

建立goroutine

建立goroutine只需在函式呼叫語句前新增go關鍵字,就可以建立併發執行單元。開發人員無需瞭解任何執行細節,排程器會自動將其安排到合適的系統執行緒上執行。

在併發程式設計裡,我們通常想將一個過程切分成幾塊,然後讓每個goroutine各自負責一塊工作。當一個程式啟動時,其主函式即在一個單獨的goroutine中執行,我們叫它main goroutine,新的goroutine會用go語句來建立,至於主和子誰先執行,由系統決定。重點:主協程退出了,其它子協程也要跟著退出;其次,主協程先退出導致子協程沒來得及呼叫

package main

import (
    "fmt"
    "time"
)

func newTask() {
    for {
        fmt.Println("this is a newTask")
        time.Sleep(time.Second) //延時1s
    }
}

func main() { //可以理解為主 goroutine

    go newTask() //新建一個協程, 新建一個任務

    for {
        fmt.Println("this is a main goroutine")
        time.Sleep(time.Second) //延時1s
    }
    //newTask()  子協程(goroutine)如果放在這會執行不到,因為程式都是單業務。for迴圈是一個任務,下面執行不到

    //go newTask() 但是前面加上go關鍵字,放到下面也沒法執行
}
this is a main goroutine
this is a newTask
this is a main goroutine
this is a newTask
this is a main goroutine
this is a newTask
this is a main goroutine
this is a newTask

主協程退出了,其它子協程也要跟著退出。

package main

import (
    "fmt"
    "time"
)

//主協程退出了,其它子協程也要跟著退出
func main() {

    go func() {
        i := 0
        for {
            i++
            fmt.Println("子協程 i = ", i)
            time.Sleep(time.Second)
        }

    }()

    i := 0
    for {
        i++
        fmt.Println("main i = ", i)
        time.Sleep(time.Second)

        if i == 2 {
            break
        }
    }

}
main i =  1
子協程 i =  1
main i =  2
子協程 i =  2

主協程先退出導致子協程沒來得及呼叫

package main

import (
    "fmt"
    "time"
)

//主協程退出了,其它子協程也要跟著退出
func main() {

    go func() {
        i := 0
        for {
            i++
            fmt.Println("子協程 i = ", i)
            time.Sleep(time.Second)
        }

    }()

}
//執行程式碼並無任何輸出,因為主協程先執行完退出

 

相關文章