TOP

go語言之并發(一)
2017-09-30 13:56:59 】 瀏覽:9272
Tags:

簡介
 
        多核處理器越來越普及,那有沒有一種簡單的辦法,能夠讓我們寫的軟件釋放多核的威力?答案是:Yes。隨著Golang, Erlang, Scale等為并發設計的程序語言的興起,新的并發模式逐漸清晰。正如過程式編程和面向對象一樣,一個好的編程模式需要有一個極其簡潔的內核,還有在此之上豐富的外延,可以解決現實世界中各種各樣的問題。本文以GO語言為例,解釋其中內核、外延。
 
并發模式之內核
 
        這種并發模式的內核只需要協程和通道就夠了。其中協程負責執行代碼,通道負責在協程之間傳遞事件。
  Go語言并發之美
    并發編程一直以來都是個非常困難的工作。要想編寫一個良好的并發程序,我們不得不了解線程, 鎖,semaphore,barrier甚至CPU更新高速緩存的方式,而且他們個個都有怪脾氣,處處是陷阱。筆者除非萬不得以,決不會自己操作這些底層 并發元素。一個簡潔的并發模式不需要這些復雜的底層元素,只需協程和通道就夠了。
      協程是輕量級的線程。在過程式編程中,當調用一個過程的時候,需要等待其執行完才返回。而調用一個協程的時候,不需要等待其執行完优乐棋牌app下载,會立即返回。協程十分輕量,Go語言可以在一個進程中執行有數以十萬計的協程,依舊保持高性能。而對于普通的平臺,一個進程有數千個線程,其CPU會忙于上下文切換,性能急劇下降。隨意創建線程可不是一個好主意,但是我們可以大量使用的協程。

        通道是協程之間的數據傳輸通道。通道可以在眾多的協程之間傳遞數據,具體可以值也可以是個引用。通道有兩種使用方式。

        ·  協程可以試圖向通道放入數據,如果通道滿了,會掛起協程,直到通道可以為他放入數據為止。

         ·  協程可以試圖向通道索取數據,如果通道沒有數據,會掛起協程,直到通道返回數據為止。

        如此,通道就可以在傳遞數據的同時,控制協程的運行。有點像事件驅動,也有點像阻塞隊列。這兩個概念非常的簡單,各個語言平臺都會有相應的實現。在Java和C上也各有庫可以實現兩者。

  Go語言并發之美

  只要有協程和通道,就可以優雅的解決并發的問題。不必使用其他和并發有關的概念。那如何用這兩把利刃解決各式各樣的實際問題呢?

 
并發模式之外延
 
        協程相較于線程,可以大量創建。打開這扇門,我們拓展出新的用法,可以做生成器,可以讓函數返回“服務”,可以讓循環并發執行,還能共享變量。但是出現新 的用法的同時,也帶來了新的棘手問題,協程也會泄漏,不恰當的使用會影響性能。下面會逐一介紹各種用法和問題。演示的代碼用GO語言寫成,因為其簡潔明 了,而且支持全部功能。
 
1.生成器
 
       有的時候,我們需要有一個函數能不斷生成數據。比方說這個函數可以讀文件,讀網絡,生成自增長序列,生成隨機數。這些行為的特點就是,函數的已知一些變量,如文件路徑。然后不斷調用,返回新的數據。
Go語言并發之美

下面生成隨機數為例,以讓我們做一個會并發執行的隨機數生成器。

// 函數rand_generator_1 ,返回 int
funcrand_generator_1() int {
         return rand.Int()
}
//        上面是一個函數,返回一個int。假如rand.Int()這個函數調用需要很長時間等待,那該函數的調用者也會因此而掛起。所以我們可以創建一個協程,專門執行rand.Int()。


// 函數rand_generator_2,返回通道(Channel) funcrand_generator_2() chan int { // 創建通道 out := make(chan int) // 創建協程 go func() { for { //向通道內寫入數據,如果無人讀取會等待 out <- rand.Int() } }() return out } funcmain() { // 生成隨機數作為一個服務 rand_service_handler :=rand_generator_2() // 從服務中讀取隨機數并打印 fmt.Printf("%d\n",<-rand_service_handler) }

 

   上面的這段函數就可以并發執行了rand.Int()。有一點值得注意到函數的返回可以理解為一個“服務”。但我們需要獲取隨機數據時候,可以隨時向這個 服務取用,他已經為我們準備好了相應的數據,無需等待,隨要隨到。如果我們調用這個服務不是很頻繁,一個協程足夠滿足我們的需求了。但如果我們需要大量訪問,怎么辦?我們可以用下面介紹的多路復用技術,啟動若干生成器,再將其整合成一個大的服務。

        調用生成器,可以返回一個“服務”。可以用在持續獲取數據的場合。用途很廣泛,讀取數據,生成ID,甚至定時器。這是一種非常簡潔的思路,將程     序并發化。

2.多路復用

        多路復用是讓一次處理多個隊列的技術。Apache使用處理每個連接都需要一個進程,所以其并發性能不是很好。而Nginx使用多路復用的技術,讓一 個進程處理多個連接,所以并發性能比較好。同樣优乐棋牌app下载,在協程的場合,多路復用也是需要的,但又有所不同。多路復用可以將若干個相似的小服務整合成一個大服務。

Go語言并發之美

那么讓我們用多路復用技術做一個更高并發的隨機數生成器吧。

// 函數rand_generator_3 ,返回通道(Channel)
         funcrand_generator_3() chan int {
         // 創建兩個隨機數生成器服務
         rand_generator_1 := rand_generator_2()
         rand_generator_2 := rand_generator_2()
         //創建通道
         out := make(chan int)
          //創建協程
         go func() {
                   for {
                           //讀取生成器1中的數據,整合
                           out <-<-rand_generator_1
                   }
         }()
         go func() {
                   for {
                            //讀取生成器2中的數據,整合
                            out <-<-rand_generator_2
                   }
         }()
         return out
}

上面是使用了多路復用技術的高并發版的隨機數生成器。通過整合兩個隨機數生成器,這個版本的能力是剛才的兩倍。雖然協程可以大量創建,但是眾多協程還是會爭搶輸出的通道。Go語言提供了Select關鍵字來解決,各家也有各家竅門。加大輸出通道的緩沖大小

請關注公眾號獲取更多資料



首頁 上一頁 1 2 3 4 下一頁 尾頁 1/4/4
】【打印繁體】【】【】 【】【】【】 【關閉】 【返回頂部
上一篇golang 標準庫間依賴的可視化展示 下一篇Golang控制goroutine的啟動與關閉