golang並發編程

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

(一)並發基礎

1.概念

並發意味著程式在運行時有多個執行內容,對應多個調用棧。

並發與並行的區別:

並發的主流實現模型:

實現模型

說明

特點

多進程 作業系統層面的併發模式 處理簡單,互不影響,但開銷大
多線程 系統層面的併發模式 有效,開銷較大,高並發時影響效率
基於回調的非阻塞/非同步IO 多用於高並發伺服器開發中 編程複雜,開銷小
協程 使用者態線程,不需要作業系統搶佔調度,寄存於線程中 編程簡單,結構簡單,開銷極小,但需要語言的支援

共用記憶體系統:線程之間採用共用記憶體的方式通訊,通過加鎖來避免死結或資源競爭。

訊息傳遞系統:將線程間共用狀態封裝在訊息中,通過發送訊息來共用記憶體,而非通過共用記憶體來通訊。

2.協程

執行體是個抽象的概念,在作業系統中分為三個層級:進程(process),進程內的線程(thread),進程內的協程(coroutine,輕量級線程)。協程的數量級可達到上百萬個,進程和線程的數量級最多不超過一萬個。Go語言中的協程叫goroutine,Go標準庫提供的叫用作業,IO操作都會出讓CPU給其他goroutine,讓協程間的切換管理不依賴系統的線程和進程,不依賴CPU的核心數量。

3.並發通訊

並發編程的難度在於協調,協調需要通過通訊,並發通訊模型分為共用資料和訊息。共用資料即多個並發單元保持對同一個資料的引用,資料可以是記憶體資料區塊,磁碟檔案,網路資料等。資料共用通過加鎖的方式來避免死結和資源競爭。Go語言則採取訊息機制來通訊,每個並發單元是獨立的個體,有獨立的變數,不同並發單元間這些變數不共用,每個並發單元的輸入輸出只通過訊息的方式。

(二)goroutine

 

//定義調用體

func Add(x,y int){

  z:=x+y

  fmt.Println(z)

}

//go關鍵字執行調用,即會產生一個goroutine並發執行

//當函數返回時,goroutine自動結束,如果有傳回值,傳回值會自動被丟棄

go Add(1,1)

//並發執行

func main(){

  for i:=0;i<10;i++{//主函數啟動了10個goroutine,然後返回,程式退出,並不會等待其他goroutine結束

    go Add(i,i)     //所以需要通過channel通訊來保證其他goroutine可以順利執行

  }

}

(三)channel

       channel就像管道的形式,是goroutine之間的通訊方式,是進程內的通訊方式,跨進程通訊建議用分布式系統的方法來解決,例如Socket或http等通訊協定。channel是類型相關,即一個channel只能傳遞一種類型的值,在聲明時指定。

1、基本文法

//1、channel聲明,聲明一個管道chanName,該管道可以傳遞的類型是ElementType

//管道是一種複合類型,[chan ElementType],表示可以傳遞ElementType類型的管道[類似定語從句的修飾方法]

var chanName chan ElementType

var ch chan int                  //聲明一個可以傳遞int類型的管道

var m map[string] chan bool      //聲明一個map,值的類型為可以傳遞bool類型的管道

 

//2、初始化

ch:=make(chan int)   //make一般用來聲明一個複合類型,參數為複合類型的屬性

 

//3、管道寫入,把值想象成一個球,"<-"的方向,表示球的流向,ch即為管道

//寫入時,當管道已滿(管道有緩衝長度)則會導致程式堵塞,直到有goroutine從中讀取出值

ch <- value

//管道讀取,"<-"表示從管道把球倒出來賦值給一個變數

//當管道為空白,讀取資料會導致程式阻塞,直到有goroutine寫入值

value:= <-ch 

 

//4、每個case必須是一個IO操作,面向channel的操作,只執行其中的一個case操作,一旦滿足則結束select過程

//面向channel的操作無非三種情況:成功讀出;成功寫入;即沒有讀出也沒有寫入

select{

  case <-chan1:

  //如果chan1讀到資料,則進行該case處理語句

  case chan2<-1:

  //如果成功向chan2寫入資料,則進入該case處理語句

  default:

  //如果上面都沒有成功,則進入default處理流程

}

2、緩衝和逾時機制

//1、緩衝機制:為管道指定空間長度,達到類似訊息佇列的效果

c:=make(chan int,1024)  //第二個參數為緩衝區大小,與切片的空間大小類似

//通過range關鍵字來實現依次讀取管道的資料,與數組或切片的range使用方法類似

for i :=range c{

  fmt.Println("Received:",i)

}

 

//2、逾時機制:利用select只要一個case滿足,程式就繼續執行而不考慮其他case的情況的特性實現逾時機制

timeout:=make(chan bool,1)    //設定一個逾時管道

go func(){

  time.Sleep(1e9)      //設定逾時時間,等待一秒鐘

  timeout<-true        //一分鐘後往管道放一個true的值

}()

//

select {

  case <-ch:           //如果讀到資料,則會結束select過程

  //從ch中讀取資料

  case <-timeout:      //如果前面的case沒有調用到,必定會讀到true值,結束select,避免永久等待

  //一直沒有從ch中讀取到資料,但從timeout中讀取到了資料

}

3、channel的傳遞

//1、channel的傳遞,來實現Linux系統中管道的功能,以外掛程式的方式增加資料處理的流程

type PipeData struct{

  value int

  handler func(intint   //handler是屬性?

  next chan int   //可以把[chan int]看成一個整體,表示放int類型的管道

}

func handler(queue chan *PipeData){ //queue是一個存放*PipeDate類型的管道,可改變管道裡的資料區塊內容

  for data:=range queue{     //data的類型就是管道存放定義的類型,即PipeData

    data.next <- data.handler(data.value)    //該方法實現將PipeData的value值存放到next的管道中

  }

}

//2、單向channel:只能用於接收或發送資料,是對channel的一種使用限制

//單向channel的聲明

var ch1 chan int    //正常channel,可讀寫

var ch2 chan<- int  //單向唯寫channel  [chan<- int]看成一個整體,表示流入管道

var ch3 <-chan int  //單向唯讀channel  [<-chan int]看成一個整體,表示流出管道

//管道類型強制轉換

ch4:=make(chan int)     //ch4為雙向管道

ch5:=<-chan int(ch4)    //把[<-chan int]看成單向唯讀管道類型,對ch4進行強制類型轉換

ch6:=chan<- int(ch4)    //把[chan<- int]看成單向唯寫管道類型,對ch4進行強制類型轉換

func Parse(ch <-chan int){    //最小許可權原則

  for value:=range ch{

    fmt.Println("Parsing value",value)

  }

}

 

//3、關閉channel,使用內建函數close()函數即可

close(ch)

//判斷channel是否關閉

x,ok:=<-ch //ok==false表示channel已經關閉

if !ok {   //如果channel關閉,ok==false,!ok==true

  //執行體

}

(四)多核並行化與同步

//多核並行化

runtime.GOMAXPROCS(16) //設定環境變數GOMAXPROCS的值來控制使用多少個CPU核心

runtime.NumCPU() //來擷取核心數

//出讓時間片

runtime.Gosched() //在每個goroutine中控制何時出讓時間片給其他goroutine

//同步

//同步鎖

sync.Mutex //單讀單寫:佔用Mutex後,其他goroutine只能等到其釋放該Mutex

sync.RWMutex //單寫多讀:會阻止寫,不會阻止讀

RLock() //讀鎖

Lock() //寫鎖

RUnlock() //解鎖(讀鎖)

Unlock() //解鎖(寫鎖)

//全域唯一性操作

//once的Do方法保證全域只調用指定函數(setup)一次,其他goroutine在調用到此函數是會阻塞,直到once調用結束才繼續

once.Do(setup)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.