婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av

主頁 > 知識庫 > Go語言并發(fā)模型的2種編程方案

Go語言并發(fā)模型的2種編程方案

熱門標簽:外呼系統(tǒng)多少錢一年 旅游廁所如何電子地圖標注 海外照相館地圖標注入駐 滁州自建外呼系統(tǒng) 經(jīng)常接到推銷電話機器人的電話 工商信用卡外呼系統(tǒng)教程 客服級電銷機器人 外呼系統(tǒng)如何接收服務密碼 智能營銷軟件

概述

我一直在找一種好的方法來解釋 go 語言的并發(fā)模型:

不要通過共享內存來通信,相反,應該通過通信來共享內存

但是沒有發(fā)現(xiàn)一個好的解釋來滿足我下面的需求:

1.通過一個例子來說明最初的問題
2.提供一個共享內存的解決方案
3.提供一個通過通信的解決方案

這篇文章我就從這三個方面來做出解釋。

讀過這篇文章后你應該會了解通過通信來共享內存的模型,以及它和通過共享內存來通信的區(qū)別,你還將看到如何分別通過這兩種模型來解決訪問和修改共享資源的問題。

前提

設想一下我們要訪問一個銀行賬號:

復制代碼 代碼如下:

type Account interface {
  Withdraw(uint)
  Deposit(uint)
  Balance() int
}

type Bank struct {
  account Account
}

func NewBank(account Account) *Bank {
  return Bank{account: account}
}

func (bank *Bank) Withdraw(amount uint, actor_name string) {
  fmt.Println("[-]", amount, actor_name)
  bank.account.Withdraw(amount)
}

func (bank *Bank) Deposit(amount uint, actor_name string) {
  fmt.Println("[+]", amount, actor_name)
  bank.account.Deposit(amount)
}

func (bank *Bank) Balance() int {
  return bank.account.Balance()
}

因為 Account 是一個接口,所以我們提供一個簡單的實現(xiàn):

復制代碼 代碼如下:

type SimpleAccount struct{
  balance int
}

func NewSimpleAccount(balance int) *SimpleAccount {
  return SimpleAccount{balance: balance}
}

func (acc *SimpleAccount) Deposit(amount uint) {
  acc.setBalance(acc.balance + int(amount))
}

func (acc *SimpleAccount) Withdraw(amount uint) {
  if acc.balance >= int(mount) {
    acc.setBalance(acc.balance - int(amount))
  } else {
    panic("杰克窮死")
  }
}

func (acc *SimpleAccount) Balance() int {
  return acc.balance
}

func (acc *SimpleAccount) setBalance(balance int) {
  acc.add_some_latency()  //增加一個延時函數(shù),方便演示
  acc.balance = balance
}

func (acc *SimpleAccount) add_some_latency() {
  -time.After(time.Duration(rand.Intn(100)) * time.Millisecond)
}

你可能注意到了 balance 沒有被直接修改,而是被放到了 setBalance 方法里進行修改。這樣設計是為了更好的描述問題。稍后我會做出解釋。

把上面所有部分弄好以后我們就可以像下面這樣使用它啦:

復制代碼 代碼如下:

func main() {
  balance := 80
  b := NewBank(bank.NewSimpleAccount(balance))
 
  fmt.Println("初始化余額", b.Balance())
 
  b.Withdraw(30, "馬伊琍")
 
  fmt.Println("-----------------")
  fmt.Println("剩余余額", b.Balance())
}

運行上面的代碼會輸出:

復制代碼 代碼如下:

初始化余額 80
[-] 30 馬伊琍
-----------------
剩余余額 50

沒錯!

不錯在現(xiàn)實生活中,一個銀行賬號可以有很多個附屬卡,不同的附屬卡都可以對同一個賬號進行存取錢,所以我們來修改一下代碼:

復制代碼 代碼如下:

func main() {
  balance := 80
  b := NewBank(bank.NewSimpleAccount(balance))
 
  fmt.Println("初始化余額", b.Balance())
 
  done := make(chan bool)
 
  go func() { b.Withdraw(30, "馬伊琍"); done - true }()
  go func() { b.Withdraw(10, "姚笛"); done - true }()
 
  //等待 goroutine 執(zhí)行完成
  -done
  -done
 
  fmt.Println("-----------------")
  fmt.Println("剩余余額", b.Balance())
}

這兒兩個附屬卡并發(fā)的從賬號里取錢,來看看輸出結果:

復制代碼 代碼如下:

初始化余額 80
[-] 30 馬伊琍
[-] 10 姚笛
-----------------
剩余余額 70

這下把文章高興壞了:)

結果當然是錯誤的,剩余余額應該是40而不是70,那么讓我們看看到底哪兒出問題了。

問題

當并發(fā)訪問共享資源時,無效狀態(tài)有很大可能會發(fā)生。

在我們的例子中,當兩個附屬卡同一時刻從同一個賬號取錢后,我們最后得到銀行賬號(即共享資源)錯誤的剩余余額(即無效狀態(tài))。

我們來看一下執(zhí)行時候的情況:

復制代碼 代碼如下:

     處理情況
             --------------
             _馬伊琍_|_姚笛_
 1. 獲取余額     80  |  80
 2. 取錢       -30  | -10
 3. 當前剩余     50  |  70
                ... | ...
 4. 設置余額     50  ?  70  //該先設置哪個好呢?
 5. 后設置的生效了
             --------------
 6. 剩余余額        70

上面 ... 的地方描述了我們 add_some_latency 實現(xiàn)的延時狀況,現(xiàn)實世界經(jīng)常發(fā)生延遲情況。所以最后的剩余余額就由最后設置余額的那個附屬卡決定。

解決辦法

我們通過兩種方法來解決這個問題:

1.共享內存的解決方案
2.通過通信的解決方案

所有的解決方案都是簡單的封裝了一下 SimpleAccount 來實現(xiàn)保護機制。

共享內存的解決方案

又叫 “通過共享內存來通信”。

這種方案暗示了使用鎖機制來預防同時訪問和修改共享資源。鎖告訴其它處理程序這個資源已經(jīng)被一個處理程序占用了,因此別的處理程序需要排隊直到當前處理程序處理完畢。

讓我們來看看 LockingAccount 是怎么實現(xiàn)的:

復制代碼 代碼如下:

type LockingAccount struct {
  lock    sync.Mutex
  account *SimpleAccount
}

//封裝一下 SimpleAccount
func NewLockingAccount(balance int) *LockingAccount {
  return LockingAccount{account: NewSimpleAccount(balance)}
}

func (acc *LockingAccount) Deposit(amount uint) {
  acc.lock.Lock()
  defer acc.lock.Unlock()
  acc.account.Deposit(amount)
}

func (acc *LockingAccount) Withdraw(amount uint) {
  acc.lock.Lock()
  defer acc.lock.Unlock()
  acc.account.Withdraw(amount)
}

func (acc *LockingAccount) Balance() int {
  acc.lock.Lock()
  defer acc.lock.Unlock()
  return acc.account.Balance()
}

直接明了!注意 lock sync.Lock,lock.Lock(),lock.Unlock()。

這樣每次一個附屬卡訪問銀行賬號(即共享資源),這個附屬卡會自動獲得鎖直到最后操作完畢。

我們的 LockingAccount 像下面這樣使用:

復制代碼 代碼如下:

func main() {
  balance := 80
  b := NewBank(bank.NewLockingAccount(balance))
 
  fmt.Println("初始化余額", b.Balance())
 
  done := make(chan bool)
 
  go func() { b.Withdraw(30, "馬伊琍"); done - true }()
  go func() { b.Withdraw(10, "姚笛"); done - true }()
 
  //等待 goroutine 執(zhí)行完成
  -done
  -done
 
  fmt.Println("-----------------")
  fmt.Println("剩余余額", b.Balance())
}

輸出的結果是:

復制代碼 代碼如下:

初始化余額 80
[-] 30 馬伊琍
[-] 10 姚笛
-----------------
剩余余額 40

現(xiàn)在結果正確了!

在這個例子中第一個處理程序加鎖后獨享共享資源,其它處理程序只能等待它執(zhí)行完成。

我們接著看一下執(zhí)行時的情況,假設馬伊琍先拿到了鎖:

復制代碼 代碼如下:

處理過程
                        ________________
                        _馬伊琍_|__姚笛__
        加鎖                   >
        得到余額            80  |
        取錢               -30  |
        當前余額            50  |
                           ... |
        設置余額            50  |
        解除鎖                 >
                               |
        當前余額                50
                               |
        加鎖                   >
        得到余額                |  50
        取錢                    | -10
        當前余額                |  40
                               |  ...
        設置余額                |  40
        解除鎖                  >
                        ________________
        剩余余額                40

現(xiàn)在我們的處理程序在訪問共享資源時相繼的產(chǎn)生了正確的結果。

通過通信的解決方案

又叫 “通過通信來共享內存”。

現(xiàn)在賬號被命名為 ConcurrentAccount,像下面這樣來實現(xiàn):

復制代碼 代碼如下:

type ConcurrentAccount struct {
  account     *SimpleAccount
  deposits    chan uint
  withdrawals chan uint
  balances    chan chan int
}

func NewConcurrentAccount(amount int) *ConcurrentAccount{
  acc := ConcurrentAccount{
    account :    SimpleAccount{balance: amount},
    deposits:    make(chan uint),
    withdrawals: make(chan uint),
    balances:    make(chan chan int),
  }
  acc.listen()
 
  return acc
}

func (acc *ConcurrentAccount) Balance() int {
  ch := make(chan int)
  acc.balances - ch
  return -ch
}

func (acc *ConcurrentAccount) Deposit(amount uint) {
  acc.deposits - amount
}

func (acc *ConcurrentAccount) Withdraw(amount uint) {
  acc.withdrawals - amount
}

func (acc *ConcurrentAccount) listen() {
  go func() {
    for {
      select {
      case amnt := -acc.deposits:
        acc.account.Deposit(amnt)
      case amnt := -acc.withdrawals:
        acc.account.Withdraw(amnt)
      case ch := -acc.balances:
        ch - acc.account.Balance()
      }
    }
  }()
}

ConcurrentAccount 同樣封裝了 SimpleAccount ,然后增加了通信通道

調用代碼和加鎖版本的一樣,這里就不寫了,唯一不一樣的就是初始化銀行賬號的時候:

復制代碼 代碼如下:

b := NewBank(bank.NewConcurrentAccount(balance))

運行產(chǎn)生的結果和加鎖版本一樣:

復制代碼 代碼如下:

初始化余額 80
[-] 30 馬伊琍
[-] 10 姚笛
-----------------
剩余余額 40

讓我們來深入了解一下細節(jié)。

通過通信來共享內存是如何工作的

一些基本注意點:

共享資源被封裝在一個控制流程中。

結果就是資源成為了非共享狀態(tài)。沒有處理程序能夠直接訪問或者修改資源。你可以看到訪問和修改資源的方法實際上并沒有執(zhí)行任何改變。

復制代碼 代碼如下:

func (acc *ConcurrentAccount) Balance() int {
    ch := make(chan int)
    acc.balances - ch
    balance := -ch
    return balance
  }
  func (acc *ConcurrentAccount) Deposit(amount uint) {
    acc.deposits - amount
  }

  func (acc *ConcurrentAccount) Withdraw(amount uint) {
    acc.withdrawals - amount
  }

訪問和修改是通過消息和控制流程通信。

在控制流程中任何訪問和修改的動作都是相繼發(fā)生的。

當控制流程接收到訪問或者修改的請求后會立即執(zhí)行相關動作。讓我們仔細看看這個流程:

復制代碼 代碼如下:

func (acc *ConcurrentAccount) listen() {
    // 執(zhí)行控制流程
    go func() {
      for {
        select {
        case amnt := -acc.deposits:
          acc.account.Deposit(amnt)
        case amnt := -acc.withdrawals:
          acc.account.Withdraw(amnt)
        case ch := -acc.balances:
          ch - acc.account.Balance()
        }
      }
    }()
  }

select  不斷地從各個通道中取出消息,每個通道都跟它們所要執(zhí)行的操作相一致。

重要的一點是:在 select 聲明內部的一切都是相繼執(zhí)行的(在同一個處理程序中排隊執(zhí)行)。一次只有一個事件(在通道中接受或者發(fā)送)發(fā)生,這樣就保證了同步訪問共享資源。

領會這個有一點繞。

讓我們用例子來看看 Balance() 的執(zhí)行情況:

復制代碼 代碼如下:

 一張附屬卡的流程      |   控制流程
      ----------------------------------------------

 1.     b.Balance()         |
 2.             ch -> [acc.balances]-> ch
 3.             -ch        |  balance = acc.account.Balance()
 4.     return  balance -[ch]- balance
 5                          |

這兩個流程都干了點什么呢?

附屬卡的流程

1.調用 b.Balance()
2.新建通道 ch,將 ch 通道塞入通道 acc.balances 中與控制流程通信,這樣控制流程也可以通過 ch 來返回余額
3.等待 -ch 來取得要接受的余額
4.接受余額
5.繼續(xù)

控制流程

1.空閑或者處理
2.通過 acc.balances 通道里面的 ch 通道來接受余額請求
3.取得真正的余額值
4.將余額值發(fā)送到 ch 通道
5.準備處理下一個請求

控制流程每次只處理一個 事件。這也就是為什么除了描述出來的這些以外,第2-4步?jīng)]有別的操作執(zhí)行。

總結

這篇博客描述了問題以及問題的解決辦法,但那時沒有深入去探究不同解決辦法的優(yōu)缺點。

其實這篇文章的例子更適合用 mutex,因為這樣代碼更加清晰。

最后,請毫無顧忌的指出我的錯誤!

您可能感興趣的文章:
  • golang gin 框架 異步同步 goroutine 并發(fā)操作
  • Golang 探索對Goroutine的控制方法(詳解)
  • 關于Golang中for-loop與goroutine的問題詳解
  • go語言執(zhí)行等待直到后臺goroutine執(zhí)行完成實例分析
  • Go語言輕量級線程Goroutine用法實例
  • go獲取協(xié)程(goroutine)號的實例
  • 分析Go語言中CSP并發(fā)模型與Goroutine的基本使用

標簽:九江 運城 本溪 晉城 湘潭 喀什 楚雄 深圳

巨人網(wǎng)絡通訊聲明:本文標題《Go語言并發(fā)模型的2種編程方案》,本文關鍵詞  語言,并發(fā),模型,的,2種,;如發(fā)現(xiàn)本文內容存在版權問題,煩請?zhí)峁┫嚓P信息告之我們,我們將及時溝通與處理。本站內容系統(tǒng)采集于網(wǎng)絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《Go語言并發(fā)模型的2種編程方案》相關的同類信息!
  • 本頁收集關于Go語言并發(fā)模型的2種編程方案的相關信息資訊供網(wǎng)民參考!
  • 推薦文章
    婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av
    国产一区二区三区观看| 精品一区二区三区影院在线午夜| 亚洲欧美另类在线| 欧美男男青年gay1069videost | 日韩午夜在线观看视频| 成人v精品蜜桃久久一区| 91天堂素人约啪| 欧美视频一二三区| 26uuu另类欧美亚洲曰本| 国产欧美精品在线观看| 亚洲在线视频一区| 久久精品国产亚洲一区二区三区| 成人免费精品视频| 精品粉嫩aⅴ一区二区三区四区 | 日韩欧美一二三| 国产精品乱码一区二区三区软件| 亚洲成人精品影院| 99免费精品视频| 国产午夜久久久久| 亚洲aⅴ怡春院| 欧美日韩一区二区在线视频| 亚洲人一二三区| 另类小说综合欧美亚洲| 欧美色倩网站大全免费| 久久精品欧美一区二区三区不卡| 久久久久高清精品| 国产精品1024久久| 亚洲精品一线二线三线| 日韩和的一区二区| 欧美日韩一区二区三区不卡| 亚洲乱码中文字幕| 欧美羞羞免费网站| 中文字幕不卡一区| 色婷婷综合久久久| 亚洲嫩草精品久久| 欧美麻豆精品久久久久久| 欧美激情中文不卡| 国产一区激情在线| 成人av网址在线| 欧美日韩在线一区二区| 色诱亚洲精品久久久久久| 欧美国产日韩亚洲一区| 黄页视频在线91| 国产午夜精品久久久久久免费视| 国产在线播放一区三区四| 中文字幕精品一区二区精品绿巨人| 亚洲一级二级在线| 日产欧产美韩系列久久99| 国产精品欧美经典| 91免费在线看| 亚洲成人激情自拍| 在线免费观看不卡av| 亚洲天堂免费看| 色婷婷国产精品久久包臀| 综合久久久久久| 国产一区二区三区久久久| 国产精品久久久久久久蜜臀| 国内精品嫩模私拍在线| 国产精品国产三级国产| 亚洲欧美日本韩国| 欧美一级久久久| 91精品国产综合久久香蕉的特点 | 国内精品免费在线观看| 国产乱码字幕精品高清av| 在线视频国内一区二区| 激情都市一区二区| 久久久高清一区二区三区| 欧美色网站导航| 婷婷六月综合网| 久久久精品tv| 亚洲免费看黄网站| 麻豆国产91在线播放| 一区二区三区精品在线观看| 久久爱另类一区二区小说| 激情图片小说一区| 国产精品乱码久久久久久| 欧美电影免费观看高清完整版| 中国色在线观看另类| 色婷婷综合久久久久中文| 东方欧美亚洲色图在线| 久色婷婷小香蕉久久| 午夜精品一区二区三区电影天堂 | 日本韩国精品一区二区在线观看| 91精品国产色综合久久不卡电影 | 91蝌蚪porny| 男女激情视频一区| 国产日韩一级二级三级| xfplay精品久久| 精品av综合导航| 欧美怡红院视频| 欧美日韩国产欧美日美国产精品| 久久只精品国产| 成人黄色一级视频| jlzzjlzz国产精品久久| 久久免费美女视频| 久久综合五月天婷婷伊人| 国产美女娇喘av呻吟久久| 免费精品视频最新在线| 蜜臀av性久久久久蜜臀aⅴ流畅 | 日韩欧美国产三级| 国产99久久久国产精品| 国产天堂亚洲国产碰碰| 欧美日韩国产经典色站一区二区三区| 国产高清在线精品| 日韩欧美一区电影| 国产欧美视频一区二区三区| 国产午夜精品福利| 亚洲日本丝袜连裤袜办公室| 国产精品无码永久免费888| 在线视频一区二区免费| 777午夜精品视频在线播放| 欧美精品一区二区三区久久久| 国产欧美日韩另类一区| 国产亚洲欧美一级| 中文字幕高清一区| 亚洲主播在线观看| 中国av一区二区三区| 亚洲国产美女搞黄色| 久久精品噜噜噜成人88aⅴ| 精品一区二区三区日韩| 亚洲线精品一区二区三区八戒| 婷婷一区二区三区| 美女mm1313爽爽久久久蜜臀| 岛国精品在线播放| 国产91精品一区二区麻豆网站 | 国产色产综合产在线视频| 在线视频国内一区二区| 欧美精品一区二区高清在线观看 | 亚洲人精品午夜| 国产精品1区2区3区在线观看| 色婷婷久久一区二区三区麻豆| 国产欧美日韩激情| 国产麻豆精品久久一二三| 久久不见久久见免费视频1| 欧美精品欧美精品系列| 久久精品一区二区三区不卡| 婷婷综合另类小说色区| 欧美精品在线观看一区二区| 欧美国产日产图区| 91网址在线看| 国产色一区二区| 九九在线精品视频| 欧美系列亚洲系列| 久久精品视频一区二区三区| 午夜精品国产更新| 精品少妇一区二区三区视频免付费| 亚洲午夜久久久久久久久电影院| 香蕉加勒比综合久久| 99riav久久精品riav| 日韩无一区二区| 五月天亚洲婷婷| 精品动漫一区二区三区在线观看| 国产美女一区二区| 欧美大胆人体bbbb| 成人影视亚洲图片在线| 1000部国产精品成人观看| 粉嫩高潮美女一区二区三区| 日韩欧美一区在线观看| 成人aa视频在线观看| 亚州成人在线电影| 亚洲色图视频免费播放| 欧美视频一区在线观看| 国产乱码精品一区二区三区忘忧草 | 久久久久国产一区二区三区四区| 欧美日韩精品一区二区三区四区| 久久日一线二线三线suv| 日韩va欧美va亚洲va久久| 51午夜精品国产| 美国三级日本三级久久99 | 午夜精品福利视频网站| 国产亚洲成aⅴ人片在线观看| 香港成人在线视频| 69堂精品视频| 91福利国产成人精品照片| 五月婷婷另类国产| 欧美伊人久久久久久久久影院| 成人小视频免费在线观看| 午夜精品成人在线| 亚洲曰韩产成在线| 欧美xfplay| 欧美一区二区三区视频免费播放| 久久国产精品99久久人人澡| 亚洲欧洲av色图| 欧美日本一区二区在线观看| 91浏览器打开| 国产精品1区二区.| 国产美女一区二区| 国内精品国产三级国产a久久| 国产精品久久久久久一区二区三区| 337p粉嫩大胆噜噜噜噜噜91av| 欧美老女人在线| 国产成人免费在线观看不卡| 亚洲一区二三区| 亚洲激情自拍偷拍| av电影在线不卡| 爽爽淫人综合网网站| 久久综合一区二区| 国产黄色91视频| 一区二区三区四区蜜桃| 精品国产一区二区亚洲人成毛片|