乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      22 Go常見的并發(fā)模式和并發(fā)模型

       F2967527 2021-04-28

      Go并發(fā)模型 

      傳統(tǒng)的編程語言C++ Java Python等,他們的并發(fā)邏輯多事基于操作系統(tǒng)的線程。并發(fā)執(zhí)行單元(線程)之間的通信利用的就是操作系統(tǒng)提供的線程或進程間通信的原語。如:共享內(nèi)存、信號、管道、消息隊列、套接字等。在這些通信原語中,使用最廣泛的就是共享內(nèi)存。 

      如果你使用過這種共享內(nèi)存的并發(fā)模型,其實是難用的和容易發(fā)生錯誤的,特別是在大型或復雜的業(yè)務場景中。 

      Go語言從程序設計當初,就將解決上面?zhèn)鹘y(tǒng)并發(fā)模型問題作為目標,并在新并發(fā)模型設計中借鑒注明的CSP(Communicationing Sequential Processes-通信順序進程)并發(fā)模型。 

      CSP模型目的在于簡化并發(fā)程序的編寫,讓并發(fā)程序的編寫順序與編寫順序程序一樣簡單。 

      生產(chǎn)者  —》輸出數(shù)據(jù)輸入/輸出原語》輸出數(shù)據(jù) 

      為了實現(xiàn)CSP模型,GO語言引入了Channel.Goroutine可以讀寫channel中的數(shù)據(jù),通過channelgoroutine組合連接在一起。 

      Go語言中CSP雖然是主流并發(fā)模型,但是還是支持共享內(nèi)存并發(fā)模型。主要是在sync包中的互斥鎖、讀寫鎖、條件變量、原子操作等。那么我們該如何選擇呢? 

      第一種:創(chuàng)建模式 

      通常會使用下面的方式: 

      type Worker struct { 

      func Do(f func()) chan Worker { 

          w:= make(chan Worker) 

          go func() { 

              f() 

              w<-Worker{} 

          }() 

          return w 

      func main() { 

          c:=Do(func() { 

              fmt.Print('到下班時間了...') 

          }) 

          <-c 

      Do函數(shù)內(nèi)部創(chuàng)建了一個gorutine并且返回了一個channel類型的變量。Do函數(shù)創(chuàng)建的新goroutine與調(diào)用的Do函數(shù)的goroutine之間通過一個channel聯(lián)系了起來,2goroutine可以通過channel進行通訊。Do函數(shù)的實現(xiàn)因為channelGo語言中是一等公民,channel可以像變量一樣初始化、傳遞和賦值。上面的例子Do返回了一個變量,這個變量就是通道,實現(xiàn)了主goroutine和子goroutine的通信。 

      第二種:退出模式 

          a) 分離模式 

              分離模式使用最廣泛的是goroutine退出模式。所謂分離模式就是創(chuàng)建它的goroutine不需要關心它的退出,這類goroutine啟動后與其創(chuàng)建者徹底分離,其生命周期與其執(zhí)行的主函數(shù)相關,函數(shù)返回即goroutine退出。 

              場景1:一次性任務 

      // $GOROOT/src/net/dial.go 

      func (d *Dialer) DialContext(ctx context.Context, network, address string) (Conn, error) { 

          ... ... 

              if oldCancel := d.Cancel; oldCancel != nil { 

                      subCtx, cancel := context.WithCancel(ctx) 

                      defer cancel() 

                      go func() { 

                              select { 

                              case <-oldCancel: 

                                      cancel() 

                              case <-subCtx.Done(): 

                              } 

                      }() 

                      ctx = subCtx 

              } 

          ... ... 

      DialContext方法中創(chuàng)建了一個goroutine,用來監(jiān)聽量個channel是否有數(shù)據(jù),一旦有數(shù)據(jù),處理后即退出。 

      場景2 常駐后臺執(zhí)行一些特定任務,比如常用for{…}for{select{…}}形式,還可以用定時器或事件驅動執(zhí)行。下面是Go給每個P內(nèi)置的GC goroutine就是這種場景的。

      // $GOROOT/src/runtime/mgc.go

      func gcBgMarkStartWorkers() {

              // Background marking is performed by per-P G's. Ensure that

              // each P has a background GC G.

              for _, p := range allp {

                      if p.gcBgMarkWorker == 0 {

                              go gcBgMarkWorker(p) // 這里每個P創(chuàng)建一個goroutine,以運行gcBgMarkWorker

                              notetsleepg(&work.bgMarkReady, -1)

                              noteclear(&work.bgMarkReady)

                      }

              }

      }

      func gcBgMarkWorker(_p_ *p) {

          gp := getg()

          ... ...

          for { 

                  // 處理GC

              ... ...

          }

      }

      b) join模式

      在線程模型中,父線程可以通過pthread join來等待子線程結束并獲取子線程的結束狀態(tài)。在Go中,我們有時候也有這種需求:goroutine的創(chuàng)建者需要等待新goroutine的結果。

      type Worker struct {

      }

      func Do(f func()) chan Worker {

          w:= make(chan Worker)

          go func() {

              f()

              w<-Worker{}

          }()

          return w

      }

      func main() {

          c:=Do(func() {

              fmt.Print('到下班時間了...')

          })

          <-c

      }

      我們還是看剛剛上面的這個例子,Do函數(shù)使用典型的goroutine的創(chuàng)建模式創(chuàng)建了一個groutine,maingoroutine作為創(chuàng)建通過Do函數(shù)返回的channel與新goroutine建立關系,這個channel得用途就是在goroutine之間建立退出時間的信號通信機制。main goroutine在創(chuàng)建完新goroutine后就在該channel上阻塞等待了,直到新的goroutine退出前向該channel發(fā)送了一個信號。

      運行代碼,結果如下:

      到下班時間了...

      Process finished with exit code 0

      獲取goroutine的退出狀態(tài)

      如果新goroutine的創(chuàng)建者不僅僅要等待goroutine的退出,還要知道結束狀態(tài),我們可以通過自定義類型的channel來實現(xiàn)這樣的需求。

      func add(a,b int) int{

          return a+b

      }

      func Do(f func(a,b int) int,a,b int) chan int{

          c:=make(chan int)

          go func() {

              r:=f(a,b)

              c<-r

          }()

          return c

      }

      func main() {

          c:=Do(add,1,5)

          fmt.Println(<-c)

      }

      運行結果是 6

      等待多個goroutine退出 

      func add(a,b int) int{ 

          return a+b 

      func Do(f func(a,b int) int,a,b,n int) chan int{ 

          c:=make(chan int) 

          var wg sync.WaitGroup 

          for i:=0;i<n;i++{ 

              wg.Add(1) 

              go func() { 

                  r:=f(a,b) 

                  fmt.Println(r) 

                  wg.Done() 

              }() 

          } 

          go func() { 

              wg.Wait() 

              c<-100 

          }() 

          go func() { 

          }() 

          return c 

      func main() { 

          c:=Do(add,1,5,5) 

          fmt.Println(<-c) 

      運行結果 

      100 

      c) notify-wait模式 

      前面的場景中,goroutine的創(chuàng)建者都是在被動地等待新goroutine的退出。有些場景,goroutine的創(chuàng)建者需要主動通知那些新goroutine退出。 

      通知并等待一個goroutine的退出 

      func add(a, b int) int { 

          return a + b 

      func Do(f func(a, b int) int, a, b int) chan int { 

          quit := make(chan int) 

          go func() { 

              var job chan string 

              for { 

                  select { 

                  case x := <-job: 

                      f(a, b) 

                      fmt.Println(x) 

                  case y := <-quit: 

                      quit <- y 

                  } 

              } 

          }() 

          return quit 

      func main() { 

          c := Do(add, 1, 5) 

          fmt.Println('開始干活') 

          time.Sleep(1 * time.Second) 

          c <- 0 

          timer := time.NewTimer(time.Second * 10) 

          defer timer.Stop() 

          select { 

          case status := <-c: 

              fmt.Println(status) 

          case <-timer.C: 

              fmt.Println('等待...') 

          } 

      執(zhí)行代碼結果如下 

      開始干活 

      通知并等待多個goroutine退出 

      下面是通知并等待多個goroutine退出的場景。Go語言的channel有一個特性,那就是當使用close函數(shù)關閉channel時,所有阻塞到該channel上的goroutine都會得到通知。 

      func worker(x int)  { 

          time.Sleep(time.Second * time.Duration(x)) 

      func Do(f func(a int), n int) chan int { 

          quit := make(chan int) 

          job:=make(chan int) 

          var wg sync.WaitGroup 

          for i:=0;i<n;i++ { 

              wg.Add(1) 

              go func(i int) { 

                  defer wg.Done() 

                  name := fmt.Sprintf('worker-%d',i) 

                  for { 

                      j,ok:=<-job 

                      if !ok{ 

                          fmt.Println(name,'done') 

                          return 

                      } 

                      worker(j) 

                  } 

              }(i) 

          } 

          go func() { 

              <-quit 

              close(job) 

              wg.Wait() 

              quit<-200 

          }() 

          return quit 

      func main() { 

          quit:=Do(worker,5) 

          fmt.Println('func Work...') 

          quit<-1 

          timer := time.NewTimer(time.Second * 10) 

          defer timer.Stop() 

          select { 

          case status := <-quit: 

              fmt.Println(status) 

          case <-timer.C: 

              fmt.Println('等待...') 

          } 

      運行結果 

      func Work... 

      worker-1 done 

      worker-2 done 

      worker-3 done 

      worker-4 done 

      worker-0 done 

      200 

        本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊一鍵舉報。
        轉藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多