日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

一文搞懂Go中select的隨機公平策略:并發編程的黃金法則

來源: 責編: 時間:2023-12-20 09:23:19 205觀看
導讀一、引言今天呢,咱們來聊聊 Go 語言的那點事兒,尤其是咱們在并發處理中常用的 select 語句,它可是處理并發時的一把利劍!Go 語言的 select 語句,仿佛是編程世界中的一位冷靜的裁判,當多個通道(channel)全都爭著搶話語權的時候

一、引言

今天呢,咱們來聊聊 Go 語言的那點事兒,尤其是咱們在并發處理中常用的 select 語句,它可是處理并發時的一把利劍!Z5O28資訊網——每日最新資訊28at.com

Z5O28資訊網——每日最新資訊28at.com

Go 語言的 select 語句,仿佛是編程世界中的一位冷靜的裁判,當多個通道(channel)全都爭著搶話語權的時候,它就會站出來,公平地判決誰應當先發聲。Z5O28資訊網——每日最新資訊28at.com

換句話說,select 可以在多個通道之間等待并選擇可用的通道執行操作。Z5O28資訊網——每日最新資訊28at.com

你得這么看select語句——它是并發編程領域里的一塊重要的拼圖,沒有這塊,你畫出的并發圖景就不完整。Z5O28資訊網——每日最新資訊28at.com

首先,我們來看一個簡單的示例:Z5O28資訊網——每日最新資訊28at.com

select {case <-chan1: // 操作1case data := <-chan2: // 操作2case chan3 <- data: // 操作3default: // 默認操作}

還別說,這幾行代碼,簡單明了,但它背后可是隱藏著深邃的并發處理智慧:Z5O28資訊網——每日最新資訊28at.com

  • select 可以在 channel 上進行非阻塞的收發操作;
  • 當存在可以收發的 channel 時,會直接處理該 channel 對應的 case;
  • 如果沒有任何通道準備好,它就會執行 default 子句(如果有的話);
  • 如果連 default 都沒得,那它就會那么靜靜站著,不厭其煩地等待,直到有一個通道準備好。

優雅!這是使用過 select 語句后,我心中的感嘆。就像你有了一塊功能強大的瑞士軍刀,可以靈活地應對各種野外求生的情況。Z5O28資訊網——每日最新資訊28at.com

Z5O28資訊網——每日最新資訊28at.com

在代碼中,select 語句也可以靈活地處理多個通道的并發操作,避免使用復雜的同步工具實現并發操作。Z5O28資訊網——每日最新資訊28at.com

Z5O28資訊網——每日最新資訊28at.com

二、select 機制

講科技,不能光有干巴巴的代碼堆砌,還得有歷史沉淀(反正以前歷史老師是這么教的 :)。Z5O28資訊網——每日最新資訊28at.com

而我們現在探討的是 Go 語言里的 select 思想,它最初源自于網絡 IO 模型中的 select,其精華在于 IO 多路復用。Z5O28資訊網——每日最新資訊28at.com

想象一下,有那么一刻,你需要同時傾聽來自世界各地的廣播,這可不是一件簡單的事兒。然而,這正是 go 中的 channel 和 select 的日常所在:致力于協調多個渠道的信息流,也只有在這里,才有 “通道爭鳴” 的景象。Z5O28資訊網——每日最新資訊28at.com

Z5O28資訊網——每日最新資訊28at.com

1. Go select 特性

讓我們像切洋蔥一樣,一層層地剝開 select 神秘的外衣:Z5O28資訊網——每日最新資訊28at.com

  • 每個案例必須是個通道:這是規定,沒得商量,像是一場形式各異的對話,總得有人發聲,對吧?
  • 所有被發送的通道表達式先被求值:這就像是通道們排著隊,等待裁判 select 逐一審視。
  • 如果有多個符合條件,select 公平地隨機挑一個執行:這也是 select 魅力之一所在,我們下文會從代碼層面探討這個特性。
  • 如果沒有通道準備好,執行 default 子句(如果有的話):和網絡選擇一樣,咱不能干等著,得找點事做。
  • 沒有 default,select 就等著,也許數秒,也許是永恒:如果沒有 default,那也只能干等著了,考驗裁判耐心的時刻。
  • 通道關閉時,讀取會導致死循環:這像是一個已經倒閉的電臺,但你的收音機還在不斷嘗試調頻接收信號。
  • 空的 select 會造成死鎖:這就是沒有對話的對話框,靜默的無聲世界。

Z5O28資訊網——每日最新資訊28at.com

2. 特性驗證

接下來,咱們通過一系列實驗來檢驗真實世界中 select 的行為。Z5O28資訊網——每日最新資訊28at.com

(1) select 已關閉通道和空通道場景Z5O28資訊網——每日最新資訊28at.com

再來看以下代碼:Z5O28資訊網——每日最新資訊28at.com

func main() {    c1, c2, c3 := make(chan bool), make(chan bool), make(chan bool)    go func() {        for {            select {                // 保證c1一定不會關閉,否則會死循環此case                case <-c1:                    fmt.Println("case c1")                // c2可以防止出現c1死循環的情況                // 如果c2關閉了(ok==false),將其置為nil,下次就會忽略此case                case _, ok := <-c2:                if !ok {                    fmt.Println("c2 is closed")                    c2 = nil                }                // 如果c3已關閉,則panic(不能往已關閉的channel里寫數據)                // 如果c3為nil,則ignore該case                case c3 <- true:                    fmt.Println("case c3")               case v <- c4:                   fmt.Println(v)            }        }    }()    time.Sleep(10 * time.Second)}

當 channel 關閉以后,case <- chan 會接收該通信對應數據類型的零值,所以會出現死循環。Z5O28資訊網——每日最新資訊28at.com

Z5O28資訊網——每日最新資訊28at.com

(2) 帶 default 語句實現非阻塞讀寫Z5O28資訊網——每日最新資訊28at.com

select {   case <- c1:   fmt.Println(":case c3")   // 當c1沒有消息時,不會一直阻塞,而是進入default   default:   fmt.Println(":select default")}

注意,Go 語言的 select 和 Java 或者 C 語言的 switch 還不太一樣:switch 中一般會帶有 default 判斷分支,但 select 使用時,外層的 for 循環和 default 不會同時出現,否則會發生死鎖。Z5O28資訊網——每日最新資訊28at.com

Z5O28資訊網——每日最新資訊28at.com

(3) select 實現定時任務Z5O28資訊網——每日最新資訊28at.com

func main() {   done := make(chan bool)   var selectTest = func() {      for {         select {            case <-time.After(1 * time.Second):            fmt.Println("Working...")            case <-done:            fmt.Println("Job done!")        }    }  }   go selectTest()   time.Sleep(3 * time.Second)   done <- true   time.Sleep(500 * time.Microsecond)}

這個例子模擬的是一個簡易的定時器,每隔一秒鐘它都會打印 "Working..." 直到我們通過關閉 done 通道告訴它 "任務完成"。Z5O28資訊網——每日最新資訊28at.com

這樣的模式在你需要定時檢查或者定時執行一些任務時非常有用!Z5O28資訊網——每日最新資訊28at.com

代碼運行結果:Z5O28資訊網——每日最新資訊28at.com

Working...Z5O28資訊網——每日最新資訊28at.com

Working...Z5O28資訊網——每日最新資訊28at.com

Job done!Z5O28資訊網——每日最新資訊28at.com

Z5O28資訊網——每日最新資訊28at.com

注意,如果定時器的另外 case 分支是上面已關閉 channel 場景,可能會出現異常,如下所示:Z5O28資訊網——每日最新資訊28at.com

func main() {    done := make(chan bool)    t := time.Now()    var selectTest = func() {        for {            select {                case <-time.After(100 * time.Microsecond):                    fmt.Println(time.Since(t), " time.After exec, return!")                    return                case <-done:                    fmt.Println("over")            }    }}    // 關閉 chan    close(done)    go selectTest()    time.Sleep(2 * time.Second)}

我們在并發執行之前就 close(done) 關閉了 Channel,不妨猜一下這段代碼會輸出什么,答案是:Z5O28資訊網——每日最新資訊28at.com

...Z5O28資訊網——每日最新資訊28at.com

overZ5O28資訊網——每日最新資訊28at.com

overZ5O28資訊網——每日最新資訊28at.com

overZ5O28資訊網——每日最新資訊28at.com

601.3938ms  time.After exec, return!Z5O28資訊網——每日最新資訊28at.com

Z5O28資訊網——每日最新資訊28at.com

這是因為:done 已經被關閉了,所以當執行 case <-done 語句時會死循環此 case 分支。Z5O28資訊網——每日最新資訊28at.com

但是,為什么還會執行退出 case,而且 return 時,時間來到了 601.3938ms 呢?Z5O28資訊網——每日最新資訊28at.com

從上面代碼中定時器 case 100 ms 執行一次,我們不難得知,程序退出時是第 6 次執行 select 語句,這里面究竟有什么魔法呢?Z5O28資訊網——每日最新資訊28at.com

Z5O28資訊網——每日最新資訊28at.com

讓我們接著往下看!Z5O28資訊網——每日最新資訊28at.com

Z5O28資訊網——每日最新資訊28at.com

3.多個 case 滿足讀寫條件

上文已經描述過,如果多個 case 滿足讀取條件時,select 會隨機選擇一個語句執行。Z5O28資訊網——每日最新資訊28at.com

讓我們用代碼來詳細描述一下:Z5O28資訊網——每日最新資訊28at.com

func main() {   done := make(chan int, 1024)   tick := time.NewTicker(time.Second)   var selectTest = func() {      for i := 0; i < 10; i++ {         select {            case done <- i:            fmt.Println(i, ", done exec")            case <- tick.C:            fmt.Println(i, ", time.After exec")        }         time.Sleep(500 * time.Millisecond)    }  }   go selectTest()   time.Sleep(5 * time.Second)}

這個例子開啟了一個 goroutine 協程來運行 selectTest 函數,在函數里面 for 循環 10 次執行 select 語句。并且,select 的兩個分支 case done <- i 和 case <- tick.C 都是可以執行的。Z5O28資訊網——每日最新資訊28at.com

這時候,我們看一下執行結果:Z5O28資訊網——每日最新資訊28at.com

0 , done execZ5O28資訊網——每日最新資訊28at.com

1 , done execZ5O28資訊網——每日最新資訊28at.com

2 , time.After execZ5O28資訊網——每日最新資訊28at.com

3 , done execZ5O28資訊網——每日最新資訊28at.com

4 , time.After execZ5O28資訊網——每日最新資訊28at.com

5 , done execZ5O28資訊網——每日最新資訊28at.com

6 , done execZ5O28資訊網——每日最新資訊28at.com

7 , done execZ5O28資訊網——每日最新資訊28at.com

8 , time.After execZ5O28資訊網——每日最新資訊28at.com

9 , done execZ5O28資訊網——每日最新資訊28at.com

Z5O28資訊網——每日最新資訊28at.com

注意,以上結果多次運行的打印順序可能不一致,是正常現象!Z5O28資訊網——每日最新資訊28at.com

我們可以發現,原本寫入 done 通道的 2、4 和 8 不見了,說明在循環的過程中,select 的兩個分支 case done <- i 和 case <- tick.C 都是執行了的。Z5O28資訊網——每日最新資訊28at.com

因此,這就驗證了當多個 case 同時滿足時,select 會隨機選擇一個執行。這個設計是為了避免某個 case 出現饑餓問題,保證公平競爭而引入的。Z5O28資訊網——每日最新資訊28at.com

Z5O28資訊網——每日最新資訊28at.com

試想一下,如果某個 case 一直執行,而某些 case 一直得不到執行,這和 select 公平選擇的初衷就沖突了。Z5O28資訊網——每日最新資訊28at.com

所以,Go 在十多年前新增 select 提交時就用了這種隨機策略并保留至今,雖然中途有過細微的變更,但整體語義一直沒有變化。Z5O28資訊網——每日最新資訊28at.com

Z5O28資訊網——每日最新資訊28at.com

三、底層原理

Go語言中 select用于處理多個通道(channel)的發送和接收操作,但在 Go 語言的源代碼中沒有直接對應的結構體。Z5O28資訊網——每日最新資訊28at.com

因此,select通過runtime.scase結構體表示其中的每個 case,該結構體包含指向通道和數據元素的指針:Z5O28資訊網——每日最新資訊28at.com

type scase struct {c    *hchan         // chanelem unsafe.Pointer // data element}

Z5O28資訊網——每日最新資訊28at.com

1.編譯器

編譯時,select 語句被轉換成 OSELECT 節點,持有 OCASE 節點集合,每個 OCASE 代表一個可能的操作,包括空(對應 default)。Z5O28資訊網——每日最新資訊28at.com

Z5O28資訊網——每日最新資訊28at.com

根據情況不同,編譯器會優化select的處理過程。優化處理的情況分為:Z5O28資訊網——每日最新資訊28at.com

  • 不存在任何 case 的 select:直接阻塞,使用 runtime.block 函數使當前協程(goroutine)進入永久休眠狀態。
  • 只有一個 case 的 select:改寫成單個 if 條件語句。
  • 有一個 case 是 default 的兩個 case 的 select:被視為非阻塞操作,分別優化為非阻塞發送或接收。
  • 多個 case:通過運行時函數 runtime.selectgo 處理,從幾個待執行的 case 中選擇一個。

非阻塞操作進行相應的編譯器重寫,發送使用 runtime.selectnbsend 函數進行非阻塞發送,接收方面有兩種函數處理單值和雙值接收。Z5O28資訊網——每日最新資訊28at.com

Z5O28資訊網——每日最新資訊28at.com

2.運行時

Z5O28資訊網——每日最新資訊28at.com

運行時,runtime.selectgo函數通過以下幾個步驟處理 select:Z5O28資訊網——每日最新資訊28at.com

  • 初始化階段,決定 case 的處理順序。
  • 遍歷 case,查找立即就緒的 Channel,如果有則立即處理。
  • 如果沒有立即就緒的 Channel,將當前 Goroutine 加入到所有相關 Channel 的收發隊列中,并掛起。
  • 當某個 case 就緒時(Channel 收到數據或有空間發送數據),調度器喚醒掛起的 Goroutine,查找并處理對應的 case。

Z5O28資訊網——每日最新資訊28at.com

四、小結

本文中,我們談到了 Go 語言里 select 的基本特性和實現,提到了select與直接 Channel 操作的相似性,以及通過 default 支持非阻塞收發操作。Z5O28資訊網——每日最新資訊28at.com

我們還揭示了select 底層實現的復雜性——需要編譯器和運行時支持。Z5O28資訊網——每日最新資訊28at.com

通過以上不難得知,Go 的 select 語句在不同場景下的行為和實現是比較奇妙的,這也是 Go 獨特的數據結構,其背后的設計與優化策略都需要我們對 Go 底層有著比較完善的認知。Z5O28資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-50032-0.html一文搞懂Go中select的隨機公平策略:并發編程的黃金法則

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 為什么Golang開發的軟件單文件直接丟到各種Linux系統就能運行?

下一篇: 如何使用pdfplumber庫提取PDF文檔中的表格數據,并將其導出為Excel文件?

標簽:
  • 熱門焦點
  • 一加Ace2 Pro真機揭曉 鈦空灰配色質感拉滿

    終于,在經過了幾波預熱之后,一加Ace2 Pro的外觀真機圖在網上出現了。還是博主數碼閑聊站曝光的,這次的外觀設計還是延續了一加11的方案,只是細節上有了調整,例如新加入了鈦空灰
  • K60至尊版剛預熱 一加Ace2 Pro正面硬剛

    Redmi這邊剛如火如荼的宣傳了K60 Ultra的各種技術和硬件配置,作為競品的一加也坐不住了。一加中國區總裁李杰發布了兩條微博,表示在自家的一加Ace2上早就已經采用了和PixelWo
  • Raft算法:保障分布式系統共識的穩健之道

    1. 什么是Raft算法?Raft 是英文”Reliable、Replicated、Redundant、And Fault-Tolerant”(“可靠、可復制、可冗余、可容錯”)的首字母縮寫。Raft算法是一種用于在分布式系統
  • Rust中的高吞吐量流處理

    作者 | Noz編譯 | 王瑞平本篇文章主要介紹了Rust中流處理的概念、方法和優化。作者不僅介紹了流處理的基本概念以及Rust中常用的流處理庫,還使用這些庫實現了一個流處理程序
  • 最“俊美”淘寶賣家,靠直播和短視頻圈粉,上架秒光,年銷3000萬

    來源 | 電商在線文|易琬玉編輯|斯問受訪店鋪:Ringdoll戒之人形圖源:微博@御座的黃山、&ldquo;Ringdoll戒之人形&rdquo;淘寶店鋪有關外貌的評價,黃山已經聽累了。生于1985年的他,哪
  • 騰訊VS網易,最卷游戲暑期檔,誰能笑到最后?

    作者:無銹缽來源:財經無忌7月16日晚,上海1862時尚藝術中心。伴隨著幻象的精準命中,碩大的熒幕之上,比分被定格在了14:12,被寄予厚望的EDG戰隊以絕對的優勢戰勝了BLG戰隊,拿下了總決
  • 阿里大調整

    來源:產品劉有媒體報道稱,近期淘寶天貓集團啟動了近年來最大的人力制度改革,涉及員工績效、層級體系等多個核心事項,目前已形成一個初步的&ldquo;征求意見版&rdquo;:1、取消P序列
  • 疑似小米14外觀設計圖曝光:后置相機模組變化不大

    下半年的大幕已經開啟,而誰將成為下半年手機圈的主角就成為了大家關注的焦點,其中被傳有望拿下新一代驍龍8 Gen3旗艦芯片的小米14系列更是備受大家矚
  • 超閉合精工鉸鏈 徹底消滅縫隙 三星Galaxy Z Flip5與Galaxy Z Fold5發布

    2023年7月26日,三星電子正式發布了Galaxy Z Flip5與Galaxy Z Fold5。三星新一代折疊屏手機采用超閉合精工鉸鏈,讓折疊后的縫隙不再可見。同時,配合處
Top 主站蜘蛛池模板: 宁晋县| 西乌| 岐山县| 贵港市| 万州区| 喀喇沁旗| 景东| 孟州市| 永清县| 紫金县| 崇文区| 郑州市| 鄂伦春自治旗| 孟州市| 璧山县| 安国市| 肃南| 慈溪市| 上高县| 蒙山县| 吉木萨尔县| 廉江市| 黑水县| 保靖县| 武鸣县| 乃东县| 工布江达县| 天气| 扎兰屯市| 容城县| 阿勒泰市| 色达县| 太仆寺旗| 特克斯县| 湟中县| 上栗县| 鄂伦春自治旗| 灵川县| 临夏县| 扬州市| 资源县|