Go 阻塞

阻塞?

在Go語言中,阻塞通常指的是一個goroutine(輕量級線程)在等待另一個goroutine完成操作(如I/O操作、channel通信等)時,暫時停止執行的現象。Go語言提供了多種同步和通信機制,可以用于實現阻塞的效果。

使用 Channel 實現阻塞

Channel 是Go語言中的一個核心特性,用于在goroutines之間進行通信。通過channel,你可以實現阻塞等待數據或命令。

package mainimport ("fmt""time"
)func main() {c := make(chan struct{})go func() {fmt.Println("業務處理~~~")time.Sleep(2 * time.Second)fmt.Println("業務處理完成~~~")close(c) // 關閉channel,通知工作完成}()<-c // 阻塞等待channel關閉fmt.Println("處理其他業務~~~")
}

使用 WaitGroup 實現阻塞

WaitGroup 是Go語言中用于同步一組并發操作的另一個工具。它通過計數器來跟蹤完成的操作數量。

package mainimport ("fmt""strconv""sync""time"
)func main() {var wg sync.WaitGroup //控制并發組doWork := func(i int) {// wg.Done(): 表示一個事件已經完成。它等價于 wg.Add(-1),但更明確地表達了“完成一個任務”的意圖,并且在使用上更安全,因為它不會導致計數變為負數(如果已經到達零,則會panic)。defer wg.Done() // 當函數返回時,通知WaitGroup一個操作已完成相當于wg.Add(-1)fmt.Println("處理業務~~~" + strconv.Itoa(i))time.Sleep(2 * time.Second)fmt.Println("業務處理完成~~~" + strconv.Itoa(i))}for i := 0; i < 5; i++ {wg.Add(1)    // 增加WaitGroup的計數器go doWork(i) // 啟動一個goroutine做工作}//主goroutine調用wg.Wait(),直到所有啟動的goroutines都通過調用wg.Done()通知它們已經完成工作wg.Wait() // 阻塞,直到WaitGroup的計數器為0fmt.Println("所有業務處理完成~~~")
}

使用 Mutex 和 Conditional Variables 實現阻塞

Mutex(互斥鎖)和條件變量可以用來同步訪問共享資源,并實現基于條件的阻塞。?

package mainimport ("fmt""sync""time"
)func main() {var mtx sync.Mutex         //創建互斥鎖cond := sync.NewCond(&mtx) //使用mtx作為底層互斥鎖ready := false// 啟動一個 goroutine 來改變條件變量 ready 的值,并通知 cond。go func() {fmt.Println("循環跟goroutine是go內部決定先調度的--------------------goroutine--------------------")time.Sleep(3 * time.Second)mtx.Lock() //使用互斥鎖ready = truecond.Signal() // 喚醒至少一個等待的 goroutinemtx.Unlock()  //解鎖}()mtx.Lock() // 鎖定互斥鎖,準備進入條件等待for !ready {fmt.Println("循環跟goroutine是go內部決定先調度的--------------------阻塞--------------------")cond.Wait() // 阻塞,直到 cond.Signal() 被調用//mtx.Unlock()}mtx.Unlock() // 解鎖互斥鎖,繼續執行(此處mtx.Unlock()在for循環里面阻塞等待完成后也可以,也可以沒有,因為主線程會結束,但如果后續還需要獲取互斥鎖則必須要釋放否則報錯)fmt.Println("準備繼續~~~")
}

這里是一些關鍵的修改和注意事項:

  1. sync.Cond 的使用需要一個 sync.Mutex 作為其底層的互斥鎖。在使用 cond.Wait() 之前,必須先鎖定這個互斥鎖。

  2. cond.Wait() 調用中,當前的互斥鎖會被自動釋放,goroutine 會阻塞直到它被 cond.Signal()cond.Broadcast() 喚醒。

  3. 一旦 cond.Wait() 返回,goroutine 會重新獲取互斥鎖,然后繼續執行循環或代碼塊。

  4. cond.Signal() 調用之后,您需要在某個地方調用 mtx.Unlock() 來釋放互斥鎖,否則主 goroutine 會在 cond.Wait() 之后無法獲取到鎖。

  5. 您的代碼中,cond.Wait() 之后的 mtx.Unlock() 應該在 for 循環之外,以避免在循環的每次迭代中重復加鎖和解鎖。?

在Go語言中,sync.Mutex(互斥鎖)用于保護共享資源不被多個goroutine同時修改,以避免競態條件。sync.Cond(條件變量)與互斥鎖結合使用,可以在多個goroutine之間同步共享條件。以下是關于何時使用 mtx.Lock()mtx.Unlock() 的指導:

mtx.Lock()

  • 在訪問或修改由互斥鎖保護的共享資源之前使用。
  • 在調用?cond.Wait()?之前使用,以確保在等待條件變量時,共享資源不會被其他goroutine并發訪問。
  • 在調用?cond.Signal()?或?cond.Broadcast()?之前使用,因為這些操作需要在互斥鎖保護的臨界區內執行。

mtx.Unlock()

  • 在完成對共享資源的訪問或修改后使用。
  • 在?cond.Wait()?返回后使用,因為我們已經完成了等待期間需要的共享資源訪問,并且需要重新獲取互斥鎖以繼續執行。
  • 在不再需要互斥鎖保護當前goroutine的執行路徑時使用,以允許其他等待互斥鎖的goroutine繼續執行。

注意事項

  • 互斥鎖必須在獲取后及時釋放,否則會導致死鎖。
  • 通常,獲取互斥鎖和釋放互斥鎖成對出現,以避免忘記釋放鎖。

永久阻塞

Go 的運行時的當前設計,假定程序員自己負責檢測何時終止一個?goroutine?以及何時終止該程序。可以通過調用?os.Exit?或從?main()?函數的返回來以正常方式終止程序。而有時候我們需要的是使程序阻塞在這一行。

使用 sync.WaitGroup?

一直等待直到?WaitGroup?等于 0?

package mainimport "sync"func main() {var wg sync.WaitGroupwg.Add(1)wg.Wait()
}

空 select

?select{}是一個沒有任何?case?的?select,它會一直阻塞

package mainfunc main() {select{}
}

?死循環

雖然能阻塞,但會 100%占用一個 cpu。不建議使用

package mainfunc main() {for {}
}

?用 sync.Mutex

一個已經鎖了的鎖,再鎖一次會一直阻塞,這個不建議使用

package mainimport "sync"func main() {var m sync.Mutexm.Lock()
}

?os.Signal

系統信號量,在 go 里面也是個?channel,在收到特定的消息之前一直阻塞??

package mainimport ("os""os/signal""syscall"
)func main() {sig := make(chan os.Signal, 2)//syscall.SIGTERM 是默認的終止進程信號,通常由服務管理器(如systemd、supervisor等)發送來請求程序正常終止。//syscall.SIGINT 是中斷信號,一般由用戶按下Ctrl+C鍵觸發,用于請求程序中斷執行signal.Notify(sig, syscall.SIGTERM, syscall.SIGINT)<-sig
}
從終端發送信號
  • Ctrl+C: 在大多數Unix-like系統(包括Linux和macOS)以及Windows的命令行中,按 Ctrl+C 鍵會向當前前臺進程發送一個 SIGINT(中斷)信號。這通常是停止Go程序的快捷方式。

  • Kill命令: 如果你的程序在后臺運行,并且你知道其進程ID(PID),可以通過終端發送一個信號。例如,發送一個 SIGTERM 信號,可以使用:kill PID或者指定型號類型kill -SIGTERM PID

?從Go代碼內部發送信號
package mainimport ("os""os/signal""syscall""time"
)func main() {sig := make(chan os.Signal, 2)//syscall.SIGTERM 是默認的終止進程信號,通常由服務管理器(如systemd、supervisor等)發送來請求程序正常終止。//syscall.SIGINT 是中斷信號,一般由用戶按下Ctrl+C鍵觸發,用于請求程序中斷執行signal.Notify(sig, syscall.SIGTERM, syscall.SIGINT)go func() {time.Sleep(10 * time.Second)sig <- syscall.SIGTERM}()go func() {time.Sleep(5 * time.Second)sig <- syscall.SIGINT}()<-sig
}
使用外部工具或服務管理器

如果你的Go程序作為服務運行,可能由如systemd、supervisord等服務管理器控制,這些管理器通常提供了發送信號給托管服務的機制。具體操作需參考相應服務管理器的文檔。

空 channel 或者 nil channel?

channel?會一直阻塞直到收到消息,nil channel?永遠阻塞。?

package mainfunc main() {c := make(chan struct{})<-c
}
package mainfunc main() {var c chan struct{} //nil channel<-c
}
?總結

?注意上面寫的的代碼大部分不能直接運行,都會?panic,提示“all goroutines are asleep - deadlock!”,因為 go 的?runtime?會檢查你所有的?goroutine?都卡住了, 沒有一個要執行。

你可以在阻塞代碼前面加上一個或多個你自己業務邏輯的?goroutine,這樣就不會?deadlock?了。

?

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

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

相關文章

數據賦能(86)——數據要素:管理核心框架

數據管理的核心框架是一個綜合性的體系&#xff0c;旨在確保數據的有效利用、安全性以及合規性。這個框架主要包含了以下幾個關鍵組成部分&#xff1a; 數據治理策略與目標&#xff1a;明確數據管理的整體戰略和目標&#xff0c;包括數據價值的釋放、數據資產地位的確定、多元…

OpenHarmony 實戰開發——移植通信子系統

通信子系統目前涉及Wi-Fi和藍牙適配&#xff0c;廠商應當根據芯片自身情況進行適配。 移植指導 Wi-Fi編譯文件內容如下&#xff1a; 路徑&#xff1a;“foundation/communication/wifi_lite/BUILD.gn” group("wifi") {deps [ "$ohos_board_adapter_dir/ha…

C++基礎與深度解析 | 數組 | vector | string

文章目錄 一、數組1.一維數組2.多維數組 二、vector三、string 一、數組 1.一維數組 在C中&#xff0c;數組用于存儲具有相同類型和特定大小的元素集合。數組在內存中是連續存儲的&#xff0c;并且支持通過索引快速訪問元素。 數組的聲明&#xff1a; 數組的聲明指定了元素的…

前端人員如何理解進程和線程

進程和線程的概念&#xff1a; 進程和線程本質都是cpu工作過程的時間片。 進程可以理解為cpu在運行指令即加載保存上下文所要用的時間。也可以理解為一個應用程序運行的實例。 線程是進程中更小的單位&#xff0c;描述一段指令所需要的時間。 進程是資源分配的最小單位&#xf…

【數據結構】數組循環隊列的實現

隊列&#xff08;Queue&#xff09;是一種特殊的線性數據結構&#xff0c;它遵循FIFO&#xff08;First In First Out&#xff0c;先入先出&#xff09;的原則。隊列只允許在表的前端&#xff08;front&#xff09;進行刪除操作&#xff0c;而在表的后端&#xff08;rear&#…

MySQL中導出CSV格式數據 | Java處理CSV數據

1. 導出不帶表頭的CSV數據 SELECT dataid, recordfilename INTO OUTFILE /tmp/uk_callcenter_event3.csv FIELDS TERMINATED BY , LINES TERMINATED BY \n FROM table_name WHERE createtime > 2024-03-27 22:00:00 AND createtime < 2024-04-29 23:59:59 AND timehou…

使用selenium控制已經打開的瀏覽器,應該如何實現。

要使用Selenium控制一個已經打開的瀏覽器實例&#xff0c;你可以通過以下步驟實現&#xff0c;這里以Google Chrome瀏覽器為例&#xff1a; 步驟 1: 啟動Chrome瀏覽器并啟用遠程調試 首先&#xff0c;你需要以遠程調試模式啟動Chrome瀏覽器。這可以通過在命令行中使用特定參數來…

python下載及安裝

1、python下載地址&#xff1a; Python Releases for Windows | Python.orgThe official home of the Python Programming Languagehttps://www.python.org/downloads/windows/ 2、python安裝 &#xff08;1&#xff09; 直接點擊下載后的可執行文件.exe &#xff08;2&…

Spring Boot項目怎么集成Gitee登錄

一、背景 現在的越來越多的項目&#xff0c;需要集成第三方系統進行登錄。今天我們以Spring Boot項目集成Gitee為例&#xff0c;演示一下怎么使用Oauth2協議&#xff0c;集成第三方系統登錄。 不了解oauth2的&#xff0c;可以看我之前的文章。Ouath2是怎么實現在第三方應用認…

MySQL創建儲存過程函數

DDL CREATE TABLE student (id int(11) NOT NULL AUTO_INCREMENT COMMENT 學號,createDate datetime DEFAULT NULL COMMENT 創建時間,modifyDate datetime DEFAULT NULL COMMENT 修改時間,userName varchar(30) NOT NULL COMMENT 學生名稱,pwd varchar(36) DEFAULT NULL COMME…

代碼隨想錄算法訓練營第五十二天

今日效率低下&#xff0c;努力把題做完。做快一點&#xff01;&#xff01;&#xff01; 300.最長遞增子序列 class Solution { public:int lengthOfLIS(vector<int>& nums) {if (nums.size() 1) return 1;vector<int>dp(nums.size(),1);int result 0;for(i…

計算機畢業設計Python+Spark知識圖譜課程推薦系統 課程預測系統 課程大數據 課程數據分析 課程大屏 mooc慕課推薦系統 大數據畢業設計

1 緒 論 1.1 課題研究背景 在線教育學習平臺是學生用來進行校內或校外拓展課程學習的平臺&#xff0c;平臺需要具備在線視頻觀看&#xff0c;作業提交&#xff0c;形成性考核等功能。在學生學習的過程中&#xff0c;學校的管理者或負責教師需要了解學生的學習情況和學習狀態&…

Spring STOMP-發送消息

如果你想要從應用程序的任何地方向連接的客戶端發送消息&#xff0c;要怎么做&#xff1f;任何應用程序組件都可以向brokerChannel發送消息。要這樣做&#xff0c;最簡單方法是注入一個SimpMessagingTemplate并使用它來發送消息。通常&#xff0c;你會按類型注入它&#xff0c;…

WWW服務器搭建(2)——Apache服務器配置與管理

一、Apache簡介 1.1 關于Apache Apache HTTP Server&#xff08;簡稱Apache&#xff09;是Apache軟件基金會的一個開放源碼的Web服務器&#xff0c;可以在大多數計算機操作系統中運行&#xff0c;由于其跨平臺和安全性被廣泛使用&#xff0c;是最流行的Web服務器端軟件之一。…

01-02-5

1、單鏈表中按位置查找 a.原理 通過傳遞的位置&#xff0c;返回該位置對應的地址&#xff0c;放到主函數定義的指針變量中。 我們認為位置從&#xff1a;有數據的節點開始計數 即如下結構&#xff1a; 查找位置&#xff0c;就是返回該位置對應的空間地址。 b.代碼說明 Ⅰ…

H5嵌入原生----兼容安卓與ios

主要分UI展示&#xff0c;鍵盤&#xff0c;輸入框等等。解決bug最苦惱的問題不是沒有解決方案&#xff0c;而是你沒有找到真正的原因。再就是現象難以重現&#xff0c;每次都要發布代碼&#xff0c;然后到手機app中去測試&#xff0c;模擬。這些地方會耗費大量的精力。 一、UI…

【軟設】常見易錯題匯總

目錄 計算機系統基礎 程序語言基礎 數據結構 算法設計與分析 計算機網絡與信息安全 軟件工程基礎 開發方法&#xff08;結構化與面向對象&#xff09; 數據庫 操作系統 知識產權相關的法律法規 &#x1f92f;&#x1f92f;&#x1f92f;&#x1f92f;&#x1f92f;&#x1f9…

《系統架構設計師教程(第2版)》第10章-軟件架構的演化和維護-07-軟件架構維護

文章目錄 1. 軟件架構知識管理1.1 概念1.2 架構知識的獲取1.3 作用1.4 架構知識管理的現狀 2 軟件架構修改管理3 軟件架構版本管理4. 示例4.1 背景4.2 數據獲取4.3 數據計算4.4 結果分析4.4.1 圈復雜度 (CCN)4.4.2 扇入扇出度 (FFC)4.4.3 模塊間耦合度 (CBO)4.4.4 模塊的響應 (…

mysql group by 細節介紹

mysql中group by的用法是配合聚合函數&#xff0c;利用分組信息進行統計&#xff0c;語句如“select name,sum(id) from test group by name,number”。 先來看下表1&#xff0c;表名為test&#xff1a; 執行如下SQL語句&#xff1a; SELECT name FROM test GROUP BY name 你…

OFDM802.11a的FPGA實現(十四)data域的設計優化,擠掉axi協議傳輸中的氣泡

原文鏈接&#xff08;相關文章合集&#xff09;&#xff1a;OFDM 802.11a的xilinx FPGA實現 目錄 1.前言 2.data域的時序要求 3.Debug 1.前言 前面12篇文章詳細講述了&#xff0c;OFDM 802.11a發射部分data域的FPGA實現和驗證&#xff0c;今天對data域的設計做一個總結。在…