Go初級之九:Select 與并發控制

在Go語言中,select語句是處理并發編程的核心工具之一。它讓我們能夠優雅地管理多個通道操作,實現高效的并發控制。

1. Select 語句基礎

1.1 Select 的基本語法

package mainimport ("fmt""time"
)func main() {ch1 := make(chan string)ch2 := make(chan string)// 啟動兩個goroutine發送數據go func() {time.Sleep(2 * time.Second)ch1 <- "來自通道1的數據"}()go func() {time.Sleep(1 * time.Second)ch2 <- "來自通道2的數據"}()// 使用select等待任一通道有數據select {case msg1 := <-ch1:fmt.Println("收到:", msg1)case msg2 := <-ch2:fmt.Println("收到:", msg2)}
}

1.2 Select 的工作原理

  • select會同時監聽所有case中的通道操作
  • 哪個通道先準備好,就執行對應的case
  • 如果多個通道同時準備好,隨機選擇一個執行
  • 所有case都阻塞時,select也會阻塞

2. Select 的高級用法

2.1 超時控制

func withTimeout() {ch := make(chan string)go func() {// 模擬耗時操作time.Sleep(3 * time.Second)ch <- "操作完成"}()select {case result := <-ch:fmt.Println("成功:", result)case <-time.After(2 * time.Second):fmt.Println("操作超時")}
}

2.2 默認情況(default)

func nonBlocking() {ch := make(chan string, 1)// 嘗試讀取,不阻塞select {case msg := <-ch:fmt.Println("收到:", msg)default:fmt.Println("通道為空,不等待")}// 嘗試寫入,不阻塞select {case ch <- "數據":fmt.Println("數據寫入成功")default:fmt.Println("通道已滿,寫入失敗")}
}

2.3 結合for循環

func continuousProcessing() {ch1 := make(chan string)ch2 := make(chan string)go func() {for i := 1; i <= 5; i++ {ch1 <- fmt.Sprintf("消息%d", i)time.Sleep(time.Second)}close(ch1)}()go func() {for i := 1; i <= 3; i++ {ch2 <- fmt.Sprintf("通知%d", i)time.Sleep(2 * time.Second)}close(ch2)}()// 持續監聽兩個通道for {select {case msg, ok := <-ch1:if !ok {ch1 = nil // 關閉后設為nil,不再監聽fmt.Println("通道1已關閉")} else {fmt.Println("處理:", msg)}case notify, ok := <-ch2:if !ok {ch2 = nilfmt.Println("通道2已關閉")} else {fmt.Println("通知:", notify)}}// 當兩個通道都關閉時退出if ch1 == nil && ch2 == nil {break}}
}

3. 并發控制模式

3.1 信號量模式

func semaphore() {const maxWorkers = 3semaphore := make(chan struct{}, maxWorkers)results := make(chan string, 10)tasks := []string{"任務1", "任務2", "任務3", "任務4", "任務5"}// 啟動工作goroutinefor _, task := range tasks {go func(t string) {semaphore <- struct{}{} // 獲取信號量// 模擬工作time.Sleep(time.Second)results <- fmt.Sprintf("完成: %s", t)<-semaphore // 釋放信號量}(task)}// 收集結果for i := 0; i < len(tasks); i++ {fmt.Println(<-results)}
}

3.2 超時與取消

func timeoutAndCancel() {ch := make(chan string)ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)defer cancel()go func() {// 模擬長時間操作time.Sleep(5 * time.Second)ch <- "操作完成"}()select {case result := <-ch:fmt.Println("結果:", result)case <-ctx.Done():fmt.Println("操作被取消:", ctx.Err())}
}

3.3 多路復用

func multiplexing() {ch1 := make(chan string)ch2 := make(chan string)ch3 := make(chan string)// 模擬不同來源的數據go func() {for i := 1; i <= 3; i++ {ch1 <- fmt.Sprintf("用戶%d上線", i)time.Sleep(800 * time.Millisecond)}close(ch1)}()go func() {for i := 1; i <= 2; i++ {ch2 <- fmt.Sprintf("訂單%d創建", i)time.Sleep(1200 * time.Millisecond)}close(ch2)}()go func() {for i := 1; i <= 4; i++ {ch3 <- fmt.Sprintf("日志%d記錄", i)time.Sleep(500 * time.Millisecond)}close(ch3)}()// 統一處理所有事件for {select {case msg, ok := <-ch1:if !ok {ch1 = nil} else {fmt.Printf("[用戶事件] %s\n", msg)}case msg, ok := <-ch2:if !ok {ch2 = nil} else {fmt.Printf("[訂單事件] %s\n", msg)}case msg, ok := <-ch3:if !ok {ch3 = nil} else {fmt.Printf("[日志事件] %s\n", msg)}}if ch1 == nil && ch2 == nil && ch3 == nil {break}}
}

4. 實際應用示例

4.1 健康檢查服務

func healthCheckService() {checkInterval := time.Tick(5 * time.Second)forceCheck := make(chan struct{})stop := make(chan struct{})go func() {for {select {case <-checkInterval:fmt.Println("定時健康檢查...")performHealthCheck()case <-forceCheck:fmt.Println("強制健康檢查...")performHealthCheck()case <-stop:fmt.Println("服務停止")return}}}()// 模擬外部觸發time.Sleep(3 * time.Second)forceCheck <- struct{}{}time.Sleep(8 * time.Second)stop <- struct{}{}time.Sleep(1 * time.Second)
}func performHealthCheck() {// 模擬健康檢查邏輯fmt.Println("  ? 數據庫連接正常")fmt.Println("  ? 緩存服務正常")fmt.Println("  ? 網絡連接正常")
}

4.2 并發下載器

func concurrentDownloader() {urls := []string{"https://example.com/file1","https://example.com/file2", "https://example.com/file3","https://example.com/file4",}const maxConcurrent = 2semaphore := make(chan struct{}, maxConcurrent)results := make(chan string, len(urls))for _, url := range urls {go func(u string) {semaphore <- struct{}{}defer func() { <-semaphore }()// 模擬下載duration := time.Duration(rand.Intn(3000)+1000) * time.Millisecondtime.Sleep(duration)results <- fmt.Sprintf("下載完成: %s (%v)", u, duration)}(url)}// 收集結果,帶超時timeout := time.After(5 * time.Second)completed := 0for completed < len(urls) {select {case result := <-results:fmt.Println(result)completed++case <-timeout:fmt.Println("下載超時")return}}
}

5. 最佳實踐

5.1 錯誤處理

func robustSelect() {ch := make(chan string)errCh := make(chan error)go func() {// 可能出錯的操作if rand.Float32() < 0.3 {errCh <- fmt.Errorf("隨機錯誤發生")return}ch <- "正常結果"}()select {case result := <-ch:fmt.Println("成功:", result)case err := <-errCh:fmt.Printf("錯誤: %v\n", err)case <-time.After(3 * time.Second):fmt.Println("操作超時")}
}

5.2 資源清理

func cleanupExample() {dataCh := make(chan string)done := make(chan struct{})ticker := time.NewTicker(500 * time.Millisecond)defer ticker.Stop()go func() {for range ticker.C {select {case dataCh <- "新數據":fmt.Println("數據發送")case <-done:fmt.Println("發送器停止")returndefault:// 非阻塞發送fmt.Println("緩沖區滿,跳過")}}}()// 消費數據time.Sleep(2 * time.Second)close(done) // 通知發送器停止// 繼續消費剩余數據for {select {case data, ok := <-dataCh:if !ok {return}fmt.Printf("消費: %s\n", data)default:return // 無數據可消費}}
}

總結

select是Go并發編程的利器,主要用途包括:

  1. 多通道監聽:同時處理多個通道操作
  2. 超時控制:避免無限等待
  3. 非阻塞操作:使用default實現非阻塞
  4. 并發協調:結合信號量控制并發度
  5. 優雅退出:配合context實現取消機制

關鍵要點:

  • select是隨機選擇就緒的case
  • default用于非阻塞操作
  • 結合time.After實現超時
  • 使用nil通道來停止監聽
  • 注意資源清理和錯誤處理

掌握select的使用,能讓你寫出更健壯、更高效的并發程序。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/95670.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/95670.shtml
英文地址,請注明出處:http://en.pswp.cn/web/95670.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

使用 Acme.sh 獲取和管理免費 SSL 證書

Acme.sh 是一個開源的 Shell 腳本工具&#xff0c;支持從 Let’s Encrypt 等證書頒發機構獲取免費的 SSL/TLS 證書。它支持多種驗證方式&#xff0c;并能自動續期證書&#xff0c;適合個人網站或企業使用。 目標 同時支持&#xff0c;主域名和泛域名 安裝 Acme.sh獲取源碼 git …

docker-compose跨節點部署Elasticsearch 9.X集群

系列文章目錄 提示:這里可以添加系列文章的所有文章的目錄,目錄需要自己手動添加 例如:第一章 Python 機器學習入門之pandas的使用 提示:寫完文章后,目錄可以自動生成,如何生成可參考右邊的幫助文檔 文章目錄 系列文章目錄 前言 一、環境準備 二、遇到的問題與分析 三、配…

【面試場景題】spring應用啟動時出現內存溢出怎么排查

文章目錄一、定位 OOM 類型二、基礎排查&#xff1a;調整 JVM 參數與日志三、堆內存溢出&#xff08;Heap Space&#xff09;排查1. 分析堆轉儲文件2. 典型場景與解決四、元空間溢出&#xff08;Metaspace&#xff09;排查1. 分析類加載情況2. 典型場景與解決五、直接內存溢出&…

2025年經濟學專業女生必考證書指南:打造差異化競爭力

在數字經濟快速發展的2025年&#xff0c;經濟學專業女生面臨著諸多機遇與挑戰。單純的理論知識已經難以滿足職場需求&#xff0c;企業更看重解決實際問題的能力&#xff0c;特別是將數據轉化為商業洞察的專業技能。各類專業資質認證可以成為系統提升能力的途徑之一&#xff0c;…

【CAN通信】AUTOSAR架構下TC3xx芯片是如何將一幀CAN報文接收上來的

目錄 前言 正文 1.背景介紹 2.CAN報文硬件原理 3.CAN接收軟件實現 3.1. vCan_30_Mcan_Interrupt 3.2. vCan_30_Mcan_RxInterrupt 3.3. vCan_30_Mcan_RxBasicCanHandling 4.總結 前言 在《【CAN通信】AUTOSAR架構下TC3xx芯片是如何將一幀CAN報文發送出去的》一文中我們…

STM32H750 RTC介紹及應用

第十一章 RTC介紹及應用 1. RTC 簡介 RTC&#xff08;Real-Time Clock&#xff0c;實時時鐘&#xff09;是 STM32H750VBT6 中用于提供日歷和時鐘功能的低功耗外設&#xff0c;即使主電源關閉&#xff0c;只要 VBAT&#xff08;備份電源&#xff09;供電&#xff0c;RTC 仍能持續…

飛網自適應通信:IPv4 與 IPv6 環境下的高效互聯

一、網絡連接的難題與飛網的解決方案 在日常生活中&#xff0c;我們常常會碰到這樣的場景&#xff1a;在家用手機訪問公司電腦里的重要文件&#xff0c;或者遠程連接家里的NAS設備查看照片和視頻。這些操作都需要設備之間建立起安全又穩定的連接。然而&#xff0c;現實中的網絡…

拉格朗日多項式

最近打的幾個比賽沒意思&#xff0c;不是不會就是不會。不過比賽完后看到別人的WP&#xff0c;感覺受益匪淺。先看一個多項式&#xff1a;當輸入Xi時會得到一個Si,要求輸入一定數量的xi 來求[c] 當可以輸入的x個數與c的個數相同時&#xff0c;可以用矩陣直接求解。&#xff08;…

Vue3 + TypeScript 實現文件拖拽上傳

應用效果&#xff1a;實例代碼&#xff1a;CommonApplyBasicInfoForm.vue<script setup lang"ts" name"CommonApplyBasicInfoForm"> ...... // 選擇文件列表 const selectedFiles ref<FileList | null>(null); // 通過 FormData 對象實現文件…

2025全國大學生數學建模C題保姆級思路模型(持續更新):NIPT 的時點選擇與胎兒的異常判定

2025全國大學生數學建模C題保姆級思路模型&#xff08;持續更新&#xff09;&#xff1a;NIPT 的時點選擇與胎兒的異常判定&#xff0c;完整持續更新內容見文末名片 胎兒遺傳信息檢測與臨床決策數學建模分析講義 問題一&#xff1a;Y染色體濃度的影響因素探索——線性回歸的“偵…

【筆記】Software Engineering at Google

聚焦核心原則&#xff0c;挑取最讓我眼前一亮的實踐點&#xff0c;特別是那些能直接啟發或解決我當前工作中痛點的部分。0. 序言 最近集中精力速讀了關于 ?Google 軟件工程實踐? 的諸多資料&#xff08;包括官方出版物、工程博客、技術演講以及社區討論&#xff09;。面對 G…

無人機防風技術難點解析

防風防御模塊的技術難點主要體現在以下幾個方面風場感知與精準建模1.復雜風場的實時感知&#xff1a;風&#xff0c;尤其是近地面的風&#xff0c;常常是無規則、瞬息萬變的陣風、湍流或風切變。無人機需要通過各種傳感器&#xff08;如空速計、慣性測量單元&#xff08;IMU&am…

HarmonyOS 應用開發深度解析:ArkTS 聲明式 UI 與精細化狀態管理

好的&#xff0c;請看這篇關于 HarmonyOS 應用開發中聲明式 UI 狀態管理的技術文章。 HarmonyOS 應用開發深度解析&#xff1a;ArkTS 聲明式 UI 與精細化狀態管理 引言 隨著 HarmonyOS 4、5 的廣泛應用和 HarmonyOS NEXT 的發布&#xff0c;基于 API 12 及以上的應用開發已成為…

機器學習入門,第一個MCP示例

前面我們已經搭建了屬于自己的AI大模型&#xff1a;詳情見 https://blog.csdn.net/hl_java/article/details/150591424?spm1001.2014.3001.5501 近期MCP概念這么火&#xff0c;那么它到底是什么呢&#xff0c;借一個例子為你講解 第一步&#xff1a;理解MCP的核心價值 MCP (Mo…

flutter 中間組件自適應寬度

使用Flexible IntrinsicWidth Row(children: [Text(第一個text),IntrinsicWidth(child: ConstrainedBox(constraints: BoxConstraints(maxWidth: 200), // 最大寬度限制child: Text(中間的text可能很長也可能很短,overflow: TextOverflow.ellipsis,maxLines: 1,),),),Text(第三…

TDengine 時間函數 DAYOFWEEK 用戶手冊

DAYOFWEEK 函數使用手冊 函數描述 DAYOFWEEK 函數用于返回指定日期是一周中的第幾天。該函數遵循標準的星期編號約定&#xff0c;返回值范圍為 1-7&#xff0c;其中&#xff1a; 1 星期日 (Sunday)2 星期一 (Monday)3 星期二 (Tuesday)4 星期三 (Wednesday)5 星期四 (T…

【STM32】貪吃蛇 [階段 3] 增強模塊結構(架構優化)

這篇博客是 承接&#xff1a;【項目思維】貪吃蛇&#xff08;嵌入式進階方向&#xff09;中 聚焦于 &#x1f9f1; 階段 3&#xff1a;增強模塊結構&#xff08;架構優化&#xff09; 中的 菜單系統&#xff08;Menu System&#xff09;&#xff0c;這部分的結構優化可以學到的…

江協科技STM32學習筆記補充之004

STM32 ISP 一鍵下載電路&#xff08;按功能、邏輯與時序拆解&#xff09;

數據庫小冊(1)

1. 關系型數據庫主要考點關系型數據庫&#xff1a;架構索引鎖語法理論規范2. 如何設計一個關系型數據庫設計即模塊劃分。數據庫最主要的功能是存儲我們的數據&#xff0c;所以需要一個存儲的文件系統。我們要把涉及到的物流數據提供邏輯的形式給組織和表示出來&#xff0c;這是…

記錄收入最高的一次私活 選號網,需要大量賣號的人可能需要,比如游戲腳本批量跑的號

選號網管理后臺(上傳游戲信息、賬號信息、 查看記錄) http://124.223.214.5:180/admin1 選號網客戶端(PC/H5頁面 給客戶篩選商品用) http://124.223.214.5:181/ 該在線地址僅供低頻率測試&#xff0c;正式使用需要另外部署。 功能不滿足可以聯系開發者定制 一、項目的由來 …