golang channel初次接觸

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

goroutine之間的同步

goroutine是golang中在語言層級實現的輕量級線程,僅僅利用go就能立刻起一個新線程。多線程會引入線程之間的同步問題,經典的同步問題如生產者-消費者問題,在c,java層級需要使用鎖、訊號量進行共用資源的互斥使用和多線程之間的時序控制,而在golang中有channel作為同步的工具。

1. channel實現兩個goroutine之間的通訊

package mainimport "strconv"import "fmt"func main() {taskChan := make(chan string, 3)doneChan := make(chan int, 1)for i := 0; i < 3; i++ {taskChan <- strconv.Itoa(i)fmt.Println("send: ", i)}go func() {for i := 0; i < 3; i++ {task := <-taskChanfmt.Println("received: ", task)}doneChan <- 1}()<-doneChan}
  • 建立一個channel,make(chan TYPE {, NUM}), TYPE指的是channel中傳輸的資料類型,第二個參數是可選的,指的是channel的容量大小。
  • 向channel傳入資料,CHAN <- DATA, CHAN指的是目的channel即收集資料的一方,DATA則是要傳的資料。
  • 啟動一個goroutine接收main routine向channel發送的資料,go func(){ BODY }()建立一個線程運行一個匿名函數。
  • 從channel讀取資料,DATA := <-CHAN,和向channel傳入資料相反,在資料輸送箭頭的右側的是channel,形象地展現了資料從‘隧道’流出到變數裡。
  • 通知主線程任務執行結束,doneChan的作用是為了讓main routine等待這個剛起的goroutine結束,這裡顯示了channel的另一個特性,如果從empty channel中讀取資料,則會阻塞當前routine,直到有資料可以讀取。

上面這個程式就是main routine向另一個routine發送了3條int類型的資料,當3條資料被接收到後,主線程也從阻塞狀態恢複運行,隨後結束。

2. 不要陷入“死結”

我一開始用channel的時候有報過"fatal error: all goroutines are asleep - deadlock! "的錯誤,真實的代碼是下面這樣的:

package mainimport "fmt"func main() {ch := make(chan int)ch <- 1   // I'm blocked because there is no channel read yet. fmt.Println("send")go func() {<-ch  // I will never be called for the main routine is blocked!fmt.Println("received")}()fmt.Println("over")}

我的本意是從main routine發送給另一個routine一個int型的資料,但是運行出了上述的錯誤,原因有2個:

  • 當前routine向channel發送/接收資料時,如果另一端沒有相應地接收/發送,那麼當前routine則會進行休眠。
  • 這個程式的main routine先行在ch <- 1進入休眠狀態,程式的餘下部分根本來不及運行,那麼channel裡的資料永遠不會被讀出,也就不能喚醒main routine,進入“死結”。

解決這個“死結”的方法可是是設定channel的容量大小大於1,那麼channel就不會因為資料輸入而阻塞主程; 或者將資料輸入channel的語句置於啟動新的goroutine之後。

3. channel作為狀態轉移的訊號源

我跟著MIT的分散式運算課程做了原型為Map-Reduce的課後練習,目的是實現一個Master向Worker指派任務的功能:Master伺服器去等待Worker伺服器串連註冊,Master先將Map任務和Reduce任務指派給這些註冊Worker,等待Map任務全部完成,然後將Reduce任務再分配出去,等待全部完成。

// Initialize a channel which records the process of the map jobs.mapJobChannel := make(chan string)// Start a goroutine to send the nMap(the number of the Map jobs) tasks to the main routine.go func() {for m := 0; m < nMap; m++ {// Send the "start a Map job <m>" to the receiver.mapJobChannel <- "start, " + strconv.Itoa(m)}}()// Main routine listens on this mapJobChannel for the Map job task information.nMapJobs := nMap// Process the nMap Map tasks util they're all done.for nMapJobs > 0 {// Receive the Map tasks from the channel.taskInfo := strings.Split(<-mapJobChannel, ",")state, mapNo := taskInfo[0], taskInfo[1]if state == "start" {// Assign a Map task with number mapNo to a worker.go func() {// Wait for a worker to finish the Map task.ok, workNo := assignJobToWorker("Map", mapNo)if ok {// Send a task finish signal and set the worker's state to idle.mapJobChannel <- "end, " + mapNosetWorkerState(workNo, "idle")} else {// Restart this task and set the worker's state to finished.mapJobChannel <- "start, " + mapNosetWorkerState(workNo, "finished")}}()} else {nMapJobs--}}

以上是我截取自己寫的代碼,關於用channel來傳遞當前Map任務的進度資訊,用類似訊號的方式標註當前的任務執行狀態。

  • 當從channel中讀取到"start, {NUM}"時找一個閒置Worker去執行Map任務,並且等待它的完成,完成成功則向channel中發送"end, {NUM}"訊號,表明任務完成,如果失敗,就重發"start, {NUM}"訊號。
  • 從channel中讀取到"end, {NUM}"時,把剩餘任務數減1。
    這種訊號觸發的方式,觸發Master的狀態轉移,並且可以通過增加訊號以及訊號處理的方式,拓展業務處理的情況,暫時還能處理這個需求情景。

MIT分布式系統課程

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.