Go 程式設計教學:共時性 (Concurrency)

PUBLISHED ON NOV 29, 2017 — PROGRAMMING

    由於 CPU 的時脈已經到物理上限,現在的硬體都往多核心、多 CPU 發展。同樣地,單一的大型伺服器相當昂貴,而且擴充量有限,使用多台主機組成的叢集則相對易於擴充。然而,若程式碼沒有使用共時性 (concurrency) 的特性來撰寫,則無法真正發揮平行處理 (parallel computing) 所帶來的效能提升。

    Go 主要的特色之一,就在於其對共時性程式的支援;大部分程式語言以函式庫來支援共時性程式,但 Go 將其內建在語法中。Go 的並時性程式有兩種,一種是以 CSP (communicating sequential processes) 模型的並時性程式,一種是傳統的多執行緒 (multi-thread) 程式。由於 Go 將 CSP 模型內建在語法中,通常建議使用這些內建功能來寫共時性程式。

    goroutine

    大部分的程式語言,像是 C++ 或 Java 等,以 thread 做為並行程式的單位。Go 程式以 goroutine 做為並行執行的程式碼區塊,goroutine 類似於 thread,但更輕量,一次啟動數百甚至數千個以上的 goroutine 也不會占用太多記憶體。要使用 goroutine,在函式前加上 go 關鍵字即可。以下為使用 goroutine 的實例:

    在本例中,我們建立了三個 goroutine。由於 goroutine 和主程式並時執行,若我們沒有使用 WaitGroup 將程式同步化,本程式在主程式結束時即提早結束,因此,我們宣告 wg 變數,在程式尾端等待所有 goroutine 執行結束。

    如果讀者多執行幾次本程式,會發現每次印出字串的順序不同。並時性程式和傳統的循序式程式的思維不太一樣,無法保證程式的先後順序,需注意。

    Channel

    上述的 goroutine 內的資料是各自獨立的,而 Go 用 channel 在不同並行程式間傳遞資料。如下例:

    由於通道在傳輸時,會阻塞 (blocking) 程式,在此處,我們不需要另外設置 WaitGroup。

    Buffered channel

    前述的 channel 是無緩衝的。我們也可以設置有緩衝的 (buffered) channel,buffered channel 有固定的大小,這樣就不需等待其他的 goroutine,可以直接傳送資料。

    Channel 的方向

    我們在設置 channel 時,可指定其方向,如下例:

    關閉 channel

    若不用 channel 時,可用 close 函式將 channel 關閉,如下例:

    Select

    透過 select,我們可以在多個 channel 中做選擇。如下例:

    Generator

    利用 channel 可以撰寫共時執行的 generator,如下例:

    Mutex

    除了前述的 goroutine 和 channel 外,Go 也提供較傳統的 Mutex。在共時性程式中,mutex 會將某一段程式暫時鎖住,避免兩個共時程式競爭同一塊資料。以下範例節錄自一個假想的向量類別:

    當我們要將資料存入內部的 float64 切片時,透過 Mutex 將切片暫時鎖住,避免多個程式同時存取切片。在此情形外,資料都是各自獨立的,所以,可以開啟多個 goroutine 進行並行運算。

    comments powered by Disqus