最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Golang?中的?條件變量(sync.Cond)詳解

 更新時間:2022年12月15日 08:33:52   作者:muguang_lijing  
這篇文章主要介紹了Golang?中的?條件變量(sync.Cond)詳解的相關(guān)資料,需要的朋友可以參考下

本篇文章面向的讀者: 已經(jīng)基本掌握Go中的 協(xié)程(goroutine)通道(channel),互斥鎖(sync.Mutex),讀寫鎖(sync.RWMutex) 這些知識。如果對這些還不太懂,可以先回去把這幾個知識點解決了。

首先理解以下三點再進入正題:

  • Go中的一個協(xié)程 可以理解成一個獨立的人,多個協(xié)程是多個獨立的人
  • 多個協(xié)程都需要訪問的 共享資源(比如共享變量) 可以理解成 多人要用的某種公共社會資源
  • 上鎖 其實就是加入到某個共享資源的爭搶組中;上鎖完成 就是從爭搶組中被選出,得到了期待的共享資源;解鎖 就是退出某個共享資源的爭搶組。 

假如有這樣一個現(xiàn)實場景:在一個公園中有一個公共廁所,這個廁所一次只能容納一個人上廁所,同時這個廁所中有個放卷紙的位置,其一次只能放一卷紙,一卷紙的總長度是 5 米,而每個人上一次廁所需要用掉 1 米的紙。而當(dāng)一卷紙用完后,公園管理員要負責(zé)給廁所加上一卷新紙,以便大家可以繼續(xù)使用廁所。 那么對于這個單人公共廁所,大家只能排隊上廁所,當(dāng)每個人進到廁所的時候,當(dāng)然會把廁所門鎖好,以便任何人都進不來(包括管理員)。管理員若要進到廁所查看用紙情況并加卷紙,也需要排隊(因為插隊總是不文明對吧)。

那么怎么用 Golang 去模擬上述場景呢?

首先我們先不用 sync.Cond,看如何實現(xiàn)?那么請看下面這段代碼:

package main
 
import (
    "fmt"
    "time"
    "sync"
)
 
var 卷紙 int
var m sync.Mutex
var wg sync.WaitGroup
 
func 上廁所(姓名 string){
    m.Lock()
    defer func(){
        m.Unlock()
        wg.Done()
    }()
    fmt.Printf("%s 進到廁所\t",姓名)
    if 卷紙 >= 1 {  // 進到廁所第一件事是看還有沒有紙
        fmt.Printf("正在拉屎中...\n")
        time.Sleep(time.Second)
        卷紙 -= 1
        fmt.Printf("%s 已用完廁所,正在離開\n",姓名)
        return
    }
    fmt.Printf("發(fā)現(xiàn)紙用完了,無奈先離開廁所\n")
}
 
func 加廁紙(){
    m.Lock()
    defer func(){
        m.Unlock()
        wg.Done()
    }()
    fmt.Printf("公園管理員 進到廁所\t")
    if 卷紙 <= 0 { // 管理員進到廁所是看紙有沒有用完
        fmt.Printf("公園管理員 正在加新紙...\n")
        time.Sleep(time.Millisecond*500)
        卷紙 = 5
        fmt.Printf("公園管理員 已加上新廁紙,正在離開\n")
    }else{
        fmt.Printf("發(fā)現(xiàn)紙還沒用完,先離開廁所\n")
    }
}
 
func main() {
    卷紙 = 5 // 廁所一開始就準備好了一卷紙,長度5米
    要排隊上廁所的人 := [...]string{"老王","小李","老張","小劉","阿明","欣欣","西西","芳芳"}
    for _,誰 := range 要排隊上廁所的人 {
        wg.Add(1)
        go 上廁所(誰)
    }
    wg.Add(1)
    go 加廁紙()
    wg.Wait()
}
 
/*
輸出(由于協(xié)程執(zhí)行順序的不可預(yù)測性,因此每次輸出的順序都可能不一樣):
 
公園管理員 進到廁所     發(fā)現(xiàn)紙還沒用完,先離開廁所
阿明 進到廁所   正在拉屎中...
阿明 已用完廁所,正在離開
老王 進到廁所   正在拉屎中...
老王 已用完廁所,正在離開
小劉 進到廁所   正在拉屎中...
小劉 已用完廁所,正在離開
小李 進到廁所   正在拉屎中...
小李 已用完廁所,正在離開
老張 進到廁所   正在拉屎中...
老張 已用完廁所,正在離開
欣欣 進到廁所   發(fā)現(xiàn)紙用完了,無奈先離開廁所
芳芳 進到廁所   發(fā)現(xiàn)紙用完了,無奈先離開廁所
西西 進到廁所   發(fā)現(xiàn)紙用完了,無奈先離開廁所
*/

上面的代碼已經(jīng)能看出一些效果,但還是有問題:最后三個人因為廁紙用完,都直接離開廁所后就沒有后續(xù)了?應(yīng)該是他們離開廁所后再次嘗試排隊,直到需求解決,就離開廁所不再參與排隊了,否則要不斷去排隊上廁所。而公園管理員呢,他要一直去排隊進到廁所里看還有沒有紙,而不是看一次就再也不管了。 那么請看下面的完善代碼:

package main
 
import (
    "fmt"
    "sync"
    "time"
)
 
var (
    卷紙    int
    m     sync.Mutex
    wg    sync.WaitGroup
    廁所的排隊 chan string
)
 
func 上廁所(姓名 string) {
    m.Lock() // 該語句的調(diào)用只說明本執(zhí)行體(可理解成該姓名所指的那個人)加入到了廁所資源的爭搶組中;
             // 而該語句的完成調(diào)用,才代表了從爭搶組中脫穎而出,搶到了廁所;在完成調(diào)用之前,會一直阻塞在這里(可理解為這個人正在爭搶中)
    defer func() {
        m.Unlock()
        wg.Done()
    }()
    fmt.Printf("%s 進到廁所\t", 姓名)
    if 卷紙 >= 1 { // 進到廁所第一件事是看還有沒有紙
        fmt.Printf("正在拉屎中...\n")
        time.Sleep(time.Second)
        卷紙 -= 1
        fmt.Printf("%s 已用完廁所,正在離開\n", 姓名)
        return
    }
    fmt.Printf("發(fā)現(xiàn)紙用完了,無奈先離開廁所\n")
    廁所的排隊 <- 姓名 // 再次加入廁所排隊,期望下次可以成功如廁
}
 
func 加廁紙() {
    m.Lock()
    defer m.Unlock()
    fmt.Printf("公園管理員 進到廁所\t")
    if 卷紙 <= 0 { // 管理員進到廁所是看紙有沒有用完
        fmt.Printf("公園管理員 正在加新紙...\n")
        time.Sleep(time.Millisecond * 500)
        卷紙 = 5
        fmt.Printf("公園管理員 已加上新廁紙,正在離開\n")
    } else {
        fmt.Printf("發(fā)現(xiàn)紙還沒用完,先離開廁所\n")
    }
}
 
func main() {
    卷紙 = 5                                                                // 廁所一開始就準備好了一卷紙,長度5米
    要上廁所的人 := [...]string{"老王", "小李", "老張", "小劉", "阿明", "欣欣", "西西", "芳芳"} // 這里只是舉幾個人名例子,假設(shè)此處有源源不斷的人去上廁所(讀者可以隨意改造人名來源)
    廁所的排隊 = make(chan string, len(要上廁所的人))
    for _, 誰 := range 要上廁所的人 {
        廁所的排隊 <- 誰
    }
    go func() { // 在這個執(zhí)行體中,會不斷從 廁所排隊 中把人加入到 對廁所資源的爭搶組中
        for 誰 := range 廁所的排隊 {
            wg.Add(1)
            go 上廁所(誰)
        }
    }()
    wg.Add(1)
    go func() { // 在這個執(zhí)行體中,代表公園管理員的個人時間線,他會每隔一段時間去加入爭搶組進到廁所,檢查紙還有沒有
        for {
            time.Sleep(time.Millisecond * 1200)
            加廁紙()
        }
    }()
    wg.Wait()
}
 
/*
輸出:
 
老王 進到廁所   正在拉屎中...
老王 已用完廁所,正在離開
芳芳 進到廁所   正在拉屎中...
芳芳 已用完廁所,正在離開
阿明 進到廁所   正在拉屎中...
阿明 已用完廁所,正在離開
小劉 進到廁所   正在拉屎中...
小劉 已用完廁所,正在離開
欣欣 進到廁所   正在拉屎中...
欣欣 已用完廁所,正在離開
小李 進到廁所   發(fā)現(xiàn)紙用完了,無奈先離開廁所
老張 進到廁所   發(fā)現(xiàn)紙用完了,無奈先離開廁所
西西 進到廁所   發(fā)現(xiàn)紙用完了,無奈先離開廁所
公園管理員 進到廁所     公園管理員 正在加新紙...
公園管理員 已加上新廁紙,正在離開
西西 進到廁所   正在拉屎中...
西西 已用完廁所,正在離開
小李 進到廁所   正在拉屎中...
小李 已用完廁所,正在離開
老張 進到廁所   正在拉屎中...
老張 已用完廁所,正在離開
公園管理員 進到廁所     發(fā)現(xiàn)紙還沒用完,先離開廁所
公園管理員 進到廁所     發(fā)現(xiàn)紙還沒用完,先離開廁所
公園管理員 進到廁所     發(fā)現(xiàn)紙還沒用完,先離開廁所
*/

上面這個代碼在功能上基本是完善了,成功模擬了上述 多人上公廁 的場景。但仔細一想,這個場景其實有些地方是不合常理的:如果有個人進到廁所發(fā)現(xiàn)沒紙,難道他會出來緊接著再去排隊嗎?如果排了三次五次甚至十次還是沒有紙,還要這樣不斷地反復(fù)排隊進去出來又排隊?而公園管理員,要是這樣不斷反復(fù)排隊進廁所查看,那么他這一天其他啥事都干不了。

所以更合理實際的情況應(yīng)該是:如果一個人進到廁所發(fā)現(xiàn)沒紙,他應(yīng)該先去在旁邊歇著或在附近干別的,當(dāng)公園管理員加完紙后,會通過喇叭吆喝一聲:“新紙已加上”。這樣,附近所有因為沒廁紙而歇著的人就會聽到這個通知,此時,他們再去嘗試排隊進廁所;而公園管理員也不用不斷去排隊進廁所檢查紙用完了沒有,因為經(jīng)過升級,廁所加裝了一個功能,有一個紙用盡的報警按鈕裝在紙盒旁邊,當(dāng)上完廁所的人發(fā)現(xiàn)紙用完的時候,他會先按下這個報警按鈕,再離開廁所。這個報警的聲音在整個公園的各處都可以聽到,所以管理員無論在哪里干啥,他都能收到這個紙用盡的報警信號,然后他才去進廁所加紙。

其實這種被動通知的模式就是 sync.Cond 的核心思想,它會減少資源消耗,達到更優(yōu)的效果,下面就是改良為 sync.Cond 的實現(xiàn)代碼:

package main
 
import (
    "fmt"
    "math"
    "strconv"
    "sync"
    "time"
)
 
var (
    卷紙   int
    m    sync.Mutex
    cond = sync.NewCond(&m)
)
 
func 上廁所(姓名 string) {
    m.Lock() // 該語句的調(diào)用只說明本執(zhí)行體(可理解成該姓名所指的那個人)加入到了廁所資源的爭搶組中;
             // 而該語句的完成調(diào)用,才代表了從爭搶組中脫穎而出,搶到了廁所;在完成調(diào)用之前,會一直阻塞在這里(可理解為這個人正在爭搶中)
    defer m.Unlock()
    fmt.Printf("%s 進到廁所\t", 姓名)
    for 卷紙 < 1 { // 進到廁所第一件事是看還有沒有紙
        fmt.Printf("發(fā)現(xiàn)紙用完了,先離開廁所在附近歇息等待信號\n")
        cond.Wait() // 該語句的調(diào)用 相當(dāng)于調(diào)用了 m.Unlock() 也就是退出了爭搶組,而是先歇著等待紙加上的信號;
                    // 當(dāng)收到紙加上的信號后,該語句會自動執(zhí)行 m.Lock(),也就是會重新加入到廁所的爭搶組中;
                    // 該語句的完成調(diào)用說明已經(jīng)再次成功爭搶到了廁所;
        fmt.Printf("%s 等到了廁紙已加的信號,并去再次搶到了廁所\t", 姓名)
    }
    fmt.Printf("正在拉屎中...\n")
    time.Sleep(time.Second)
    卷紙 -= 1
    fmt.Printf("%s 已用完廁所\t", 姓名)
    if 卷紙 < 1 { // 注意這里:在他用完廁所離開前,他需要看是不是紙已經(jīng)用完了,如果用完了,就按下紙用盡的報警按鈕,給公園管理員發(fā)送信號
        cond.Broadcast() // 想想,這里為什么不用 Signal() ?因為 Signal 只能通知到一個等待者,這樣就有可能通知不到 公園管理員??梢栽囍堰@里換成 Signal() 試下
        fmt.Printf("發(fā)現(xiàn)廁紙已用完,并按下了報警\t")
    }
    fmt.Printf("正在離開廁所\n")
}
 
func 加廁紙() {
    m.Lock()
    defer m.Unlock()
    fmt.Printf("公園管理員 進到廁所\t")
    for 卷紙 > 0 { // 管理員進到廁所是看紙有沒有用完
        fmt.Printf("發(fā)現(xiàn)紙還沒用完,先離開廁所在等紙用盡的報警消息\n")
        cond.Wait() // 如果紙沒用完,就先去干其他工作,等紙用盡的報警消息
        fmt.Printf("公園管理員 等到了紙用盡的報警消息,并再次搶到了廁所\n")
    }
    fmt.Printf("公園管理員 正在加新紙...\n")
    time.Sleep(time.Millisecond * 500)
    卷紙 = 5
    cond.Broadcast() // 注意:公園管理員加完新紙后,要通過喇叭喊一聲 “紙已加上” 的消息通知所有 因沒紙而等待上廁所的人
    fmt.Printf("公園管理員 已加上新廁紙,并通過喇叭通知了該消息,并正在離開廁所\n")
}
 
func main() {
    卷紙 = 5  // 廁所一開始就準備好了一卷紙,長度5米
    要上廁所的人 := [...]string{"老王", "小李", "老張", "小劉", "阿明", "欣欣", "西西", "芳芳"} // 上廁所的人名模板
    go func() { // 在這個執(zhí)行體中,代表廁所及廁所隊列的時間線,廁所永遠運營下去
        for i := 0; i < math.MaxInt; i++ { // 此循環(huán)通過編號加上上面的姓名模板來 創(chuàng)建源源不斷 上廁所的人
            for _, 人名模板 := range 要上廁所的人 {
                誰 := 人名模板 + strconv.Itoa(i)
                go 上廁所(誰)
                time.Sleep(time.Millisecond * 500) // 平均每半秒有一個人去上廁所
            }
            fmt.Printf("\n====================>> 屏幕停止輸出后,請按Enter鍵繼續(xù) <<====================\n\n")
            fmt.Scanln()
        }
    }()
    go func() { // 在這個執(zhí)行體中,代表公園管理員的個人時間線,管理員永不退休
        for {
            // 注意:相比上個版本,此處不用再加 Sleep 函數(shù)了,因為 加廁紙() 函數(shù)中的 cond.Wait() 會在有紙的時候等待信號
            加廁紙()
        }
    }()
    end := make(chan bool)
    <-end
}
 
/*
輸出:
 
公園管理員 進到廁所     發(fā)現(xiàn)紙還沒用完,先離開廁所在等紙用盡的報警消息
老王0 進到廁所  正在拉屎中...
老王0 已用完廁所        正在離開廁所
小李0 進到廁所  正在拉屎中...
小李0 已用完廁所        正在離開廁所
老張0 進到廁所  正在拉屎中...
老張0 已用完廁所        正在離開廁所
小劉0 進到廁所  正在拉屎中...
小劉0 已用完廁所        正在離開廁所
阿明0 進到廁所  正在拉屎中...
 
====================>> 屏幕停止輸出后,請按Enter鍵繼續(xù) <<====================
 
阿明0 已用完廁所        發(fā)現(xiàn)廁紙已用完,并按下了報警    正在離開廁所
欣欣0 進到廁所  發(fā)現(xiàn)紙用完了,先離開廁所在附近歇息等待信號
西西0 進到廁所  發(fā)現(xiàn)紙用完了,先離開廁所在附近歇息等待信號
芳芳0 進到廁所  發(fā)現(xiàn)紙用完了,先離開廁所在附近歇息等待信號
公園管理員 等到了紙用盡的報警消息,并再次搶到了廁所
公園管理員 正在加新紙...
公園管理員 已加上新廁紙,并通過喇叭通知了該消息,并正在離開廁所
公園管理員 進到廁所     發(fā)現(xiàn)紙還沒用完,先離開廁所在等紙用盡的報警消息
欣欣0 等到了廁紙已加的信號,并去再次搶到了廁所  正在拉屎中...
欣欣0 已用完廁所        正在離開廁所
芳芳0 等到了廁紙已加的信號,并去再次搶到了廁所  正在拉屎中...
芳芳0 已用完廁所        正在離開廁所
西西0 等到了廁紙已加的信號,并去再次搶到了廁所  正在拉屎中...
西西0 已用完廁所        正在離開廁所
 
老王1 進到廁所  正在拉屎中...
老王1 已用完廁所        正在離開廁所
小李1 進到廁所  正在拉屎中...
小李1 已用完廁所        發(fā)現(xiàn)廁紙已用完,并按下了報警    正在離開廁所
老張1 進到廁所  發(fā)現(xiàn)紙用完了,先離開廁所在附近歇息等待信號
公園管理員 等到了紙用盡的報警消息,并再次搶到了廁所
公園管理員 正在加新紙...
公園管理員 已加上新廁紙,并通過喇叭通知了該消息,并正在離開廁所
公園管理員 進到廁所     發(fā)現(xiàn)紙還沒用完,先離開廁所在等紙用盡的報警消息
小劉1 進到廁所  正在拉屎中...
小劉1 已用完廁所        正在離開廁所
阿明1 進到廁所  正在拉屎中...
 
====================>> 屏幕停止輸出后,請按Enter鍵繼續(xù) <<====================
*/

用了 sync.Cond 的代碼顯然要精簡了很多,而且還節(jié)省了計算資源,只會在收到通知的時候 才去搶公共廁所,而不是不斷地反復(fù)去搶公共廁所。通過這個對現(xiàn)實場景的模擬,我們就很容易從使用者的角度理解 sync.Cond 是什么,它的字面意思就是 “條件”,這就已經(jīng)點出了這東西的核心要義,就是滿足條件才執(zhí)行,條件是什么,信號其實就是條件,當(dāng)一個執(zhí)行體收到信號之后,它才去爭搶共享資源,否則就會掛起等待(這種等待底層其實會讓出線程,所以這種等待并不會空耗資源),比起不斷輪尋去搶資源,這種方式要節(jié)省得多。

最后留給讀者一個思考的問題:就是上面最后一版的代碼,為什么 當(dāng)紙用完后按報警按鈕通知 公園管理員 要用 sync.Broadcast() 方法去廣播通知?不是只通知管理員一個人嗎,單獨通知他不就行了,用 sync.Signal() 為什么不行?

到此這篇關(guān)于Golang 中的 條件變量(sync.Cond)詳解的文章就介紹到這了,更多相關(guān)Golang 中的 條件變量(sync.Cond)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Golang上下文Context的常見應(yīng)用場景

    Golang上下文Context的常見應(yīng)用場景

    Golang?context主要用于定義超時取消,取消后續(xù)操作,在不同操作中傳遞值。本文通過簡單易懂的示例進行說明,感興趣的可以了解一下
    2023-04-04
  • Golang實現(xiàn)異步上傳文件支持進度條查詢的方法

    Golang實現(xiàn)異步上傳文件支持進度條查詢的方法

    這篇文章主要介紹了Golang實現(xiàn)異步上傳文件支持進度條查詢的方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • 深入理解golang的基本類型排序與slice排序

    深入理解golang的基本類型排序與slice排序

    大家都知道排序有內(nèi)部排序和外部排序,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進行排序,而外部排序是因排序的數(shù)據(jù)很大,一次不能容納全部的排序記錄,在排序過程中需要訪問外存。下面就來詳細介紹golang的基本類型排序與slice排序,有需要的朋友們可以參考借鑒。
    2016-09-09
  • Go語言判斷指定文件是否存在的方法

    Go語言判斷指定文件是否存在的方法

    這篇文章主要介紹了Go語言判斷指定文件是否存在的方法,實例分析了Go語言針對文件操作的技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-02-02
  • go語言中值類型和指針類型的深入理解

    go語言中值類型和指針類型的深入理解

    這篇文章主要給大家介紹了關(guān)于go語言中值類型和指針類型的相關(guān)資料,文中通過實例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2022-03-03
  • go語言題解LeetCode1122數(shù)組的相對排序

    go語言題解LeetCode1122數(shù)組的相對排序

    這篇文章主要為大家介紹了go語言題解LeetCode1122數(shù)組的相對排序,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12
  • Golang 字符串轉(zhuǎn)time類型實現(xiàn)

    Golang 字符串轉(zhuǎn)time類型實現(xiàn)

    本文主要介紹了Golang 字符串轉(zhuǎn)time類型實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • 初步解讀Golang中的接口相關(guān)編寫方法

    初步解讀Golang中的接口相關(guān)編寫方法

    這篇文章主要介紹了Golang中的接口相關(guān)編寫方法,是Go語言入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-11-11
  • golang中的net/rpc包使用概述(小結(jié))

    golang中的net/rpc包使用概述(小結(jié))

    本篇文章主要介紹了golang中的net/rpc包使用概述(小結(jié)),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-11-11
  • Golang 基礎(chǔ)之函數(shù)使用(匿名遞歸閉包)實例詳解

    Golang 基礎(chǔ)之函數(shù)使用(匿名遞歸閉包)實例詳解

    這篇文章主要為大家介紹了Golang 基礎(chǔ)之函數(shù)使用(匿名遞歸閉包)實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-10-10

最新評論

鲁甸县| 赤水市| 定远县| 东光县| 玉林市| 揭西县| 自贡市| 襄樊市| 伊春市| 宝坻区| 雅安市| 方正县| 城市| 紫阳县| 仪征市| 灵台县| 揭阳市| 太保市| 黎川县| 长宁县| 通化市| 吉木萨尔县| 梧州市| 静安区| 邵阳市| 昆明市| 凉城县| 凯里市| 彰化县| 台南县| 黄冈市| 兴国县| 万载县| 大姚县| 彰化县| 桂东县| 黄石市| 台东市| 扬州市| 荔浦县| 太谷县|