go并發與鎖之sync.Mutex入門

sync.Mutex

原理:一個共享的變量,哪個線程握到了,哪個線程可以執行代碼

功能:一個性能不錯的悲觀鎖,使用方式和Java的ReentrantLock很像,就是手動Lock,手動UnLock。

使用例子:

var mu sync.Mutex 
var cnt int
func add() {cnt++}
var wg sync.WaitGroup// 管理協程用的,主要是讓協程同意結束后再運行調用協程
func main() {for i := 0; i < 1000; i++ {wg.Add(1)go func() {defer wg.Done()defer mu.Unlock()mu.Lock()add()}()}wg.Wait()fmt.Print(cnt)
}
實現原理:

直接看源碼好了,

func (m *Mutex) Lock() {// Fast path: grab unlocked mutex.if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) { // 能拿到鎖,就拿,拿不到就進入慢速模式if race.Enabled {race.Acquire(unsafe.Pointer(m))}return}// Slow path (outlined so that the fast path can be inlined)m.lockSlow()
}

lockSlow()是一個邏輯很多的方法,具體邏輯是:

  1. 自旋嘗試:
    • 若當前是正常模式且鎖持有時間較短,當前goroutine會自旋(循環檢查鎖狀態),嘗試避免立即阻塞。
    • 自旋條件:多核CPU、當前未處于饑餓模式、等待隊列為空或自旋次數未超過閾值。
  2. 更新等待計數:
    • 通過原子操作增加state中的等待goroutine計數(高30位)。
  3. 進入阻塞或饑餓模式:
    • 正常模式:若自旋失敗,將當前goroutine加入信號量等待隊列(sema),并調用runtime_SemacquireMutex阻塞。
    • 饑餓模式:若當前goroutine等待時間超過閾值(1ms),觸發饑餓模式。此時新來的goroutine直接進入隊列尾部,不再自旋。
func (m *Mutex) lockSlow() {// 初始化變量操作,省略...for {// 這部分處理自旋嘗試獲取鎖的邏輯if old&(mutexLocked|mutexStarving) == mutexLocked && runtime_canSpin(iter) {// 省略...runtime_doSpin()continue}new := old// 如果不是饑餓模式,嘗試獲取鎖(new |= mutexLocked)if old&mutexStarving == 0 {new |= mutexLocked}// 如果鎖已被占用或處于饑餓模式,增加等待者計數(new += 1 << mutexWaiterShift)if old&(mutexLocked|mutexStarving) != 0 {new += 1 << mutexWaiterShift}// 如果當前 goroutine 處于饑餓狀態且鎖被占用,切換到饑餓模式(new |= mutexStarving)if starving && old&mutexLocked != 0 {new |= mutexStarving}// 如果當前 goroutine 是被喚醒的:確保 mutexWoken 標志已設置(否則拋出異常);清除 mutexWoken 標志(new &^= mutexWoken)if awoke {if new&mutexWoken == 0 {throw("sync: inconsistent mutex state")}new &^= mutexWoken}// 嘗試用 CAS 更新鎖狀態if atomic.CompareAndSwapInt32(&m.state, old, new) {if old&(mutexLocked|mutexStarving) == 0 {break }// 決定排隊位置:如果是第一次等待(waitStartTime == 0),記錄開始等待時間;否則使用 LIFO 順序(queueLifo = true)queueLifo := waitStartTime != 0if waitStartTime == 0 {waitStartTime = runtime_nanotime()}// runtime_SemacquireMutex 將 goroutine 放入等待隊列并阻塞runtime_SemacquireMutex(&m.sema, queueLifo, 2)// 被喚醒后:檢查是否等待超時(超過 1ms),更新饑餓狀態;重新讀取鎖狀態starving = starving || runtime_nanotime()-waitStartTime > starvationThresholdNsold = m.state// 如果是饑餓模式:if old&mutexStarving != 0 {// 檢查狀態是否一致if old&(mutexLocked|mutexWoken) != 0 || old>>mutexWaiterShift == 0 {throw("sync: inconsistent mutex state")}// 計算狀態增量: 設置 mutexLocked;減少等待者計數;如果不再饑餓或只有一個等待者,退出饑餓模式delta := int32(mutexLocked - 1<<mutexWaiterShift)if !starving || old>>mutexWaiterShift == 1 {delta -= mutexStarving}// 原子更新狀態并退出循環atomic.AddInt32(&m.state, delta)break}awoke = trueiter = 0} else {old = m.state}}
}

在這里插入圖片描述
Goroutine A 獲取鎖(Lock()快速路徑成功)。
Goroutine B 嘗試獲取鎖,進入慢速路徑:
自旋數次后失敗,增加等待計數,進入隊列阻塞。
Goroutine A 釋放鎖(Unlock()):
喚醒Goroutine B,新來的Goroutine C可與B競爭鎖。

在這里插入圖片描述
Goroutine B 等待超過1ms,觸發饑餓模式。
Goroutine C 新到達,直接進入隊列尾部,不自旋。
Goroutine A 釋放鎖:
直接將鎖交給隊列頭部的Goroutine B。
Goroutine B 釋放鎖后,若隊列中無等待者,退出饑餓模式。

作者:ShanekAI
鏈接:https://juejin.cn/post/7488246529430487077
來源:稀土掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

整體邏輯大致來說就是:Go 的 sync.Mutex 在競爭不激烈時,會采用短暫的 自旋鎖 機制。自旋鎖允許 Goroutine 在一小段時間內忙等待,而不是立即進入阻塞狀態。這種策略避免了頻繁的上下文切換開銷。如果激烈的話,就進入饑餓模式,更改了邏輯,在饑餓模式里,停止自旋,直接將當前協程加入等待隊列。當前線程執行完畢了,如果是饑餓模式,會把隊列里第一個拿出來喚醒。

名詞解釋:

自旋:就是忙等,就是最簡單的例子:

for state == 1{} // 不停地遍歷,就好像在不停地自我旋轉一樣;直到state被其他線程修改了,才停止

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

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

相關文章

【HarmonyOS5】DevEco Studio 使用指南:代碼閱讀與編輯功能詳解

?本期內容&#xff1a;【HarmonyOS5】DevEco Studio 使用指南&#xff1a;代碼閱讀與編輯功能詳解 &#x1f3c6;系列專欄&#xff1a;鴻蒙HarmonyOS&#xff1a;探索未來智能生態新紀元 文章目錄 前言代碼閱讀代碼導航功能代碼折疊語法高亮跨語言跳轉代碼查找 快速查閱API接口…

【Python 深度學習】1D~3D iou計算

一維iou 二維 import numpy as npdef iou_1d(set_a, set_b):# 獲得集合A和B的邊界 x1, x2 set_ay1, y2 set_b# 計算交集的上下界low max(x1,y1)high - min(x2, y2)# 計算交集if high - low < 0:inter 0else:inter high - low# 計算并集union (x2 -x1) (y2 - y1) - in…

SpringBoot Controller接收參數方式, @RequestMapping

一. 通過原始的HttpServletRequest對象獲取請求參數 二. 通過Spring提供的RequestParam注解&#xff0c;將請求參數綁定給方法參數 三. 如果請求參數名與形參變量名相同&#xff0c;直接定義方法形參即可接收。(省略RequestParam) 四. JSON格式的請求參數(POST、PUT) 主要在PO…

智能防護實戰:從攻擊成本看企業安全降本增效

1. 網絡攻擊的低成本與高回報陷阱 暗網中&#xff0c;一次完整的網絡釣魚攻擊僅需30美元/月起步&#xff0c;而勒索軟件攻擊成本平均1000美元&#xff0c;卻能導致企業損失高達445萬美元&#xff08;IBM 2023年數據&#xff09;。例如&#xff0c;信用卡信息每條僅售10美元&am…

大語言模型 20 - MCP 在客戶端中使用 Cursor Cline 中配置 MCP 服務

MCP 基本介紹 官方地址&#xff1a; https://modelcontextprotocol.io/introduction “MCP 是一種開放協議&#xff0c;旨在標準化應用程序向大型語言模型&#xff08;LLM&#xff09;提供上下文的方式。可以把 MCP 想象成 AI 應用程序的 USB-C 接口。就像 USB-C 提供了一種…

MySQL 在 CentOS 7 環境下的安裝教程

&#x1f31f; 各位看官好&#xff0c;我是maomi_9526&#xff01; &#x1f30d; 種一棵樹最好是十年前&#xff0c;其次是現在&#xff01; &#x1f680; 今天來學習Mysql的相關知識。 &#x1f44d; 如果覺得這篇文章有幫助&#xff0c;歡迎您一鍵三連&#xff0c;分享給更…

WPF的基礎設施:XAML基礎語法

XAML基礎語法 1 控件聲明與屬性設置1.1 特性語法&#xff08;Attribute Syntax&#xff09;1.2 屬性元素語法&#xff08;Property Element Syntax&#xff09;1.3 特殊值標記擴展 2 x:Name與Name的區別3 注釋與代碼折疊4 實用技巧集合5 常見錯誤排查 XAML( Extensible Applic…

機器學習筆記【Week3】

一、邏輯回歸&#xff08;Logistic Regression&#xff09; 與線性回歸的區別&#xff1a; 問題類型輸出類型舉例回歸問題連續實數房價預測、氣溫預測分類問題離散類別&#xff08;0 或 1&#xff09;是否患病、是否點擊廣告、是否合格 我們希望構建一個模型&#xff0c;根據…

6.4.2_3最短路徑問題_Floyd算法

Floyd弗洛伊德 膜拜大佬&#xff0c;給大佬鞠躬鞠躬鞠躬。。。。。。。。。 Floyd算法 ----解決頂點間的最短路徑&#xff1a; 過程&#xff1a; 如下&#xff1a; 初始化(沒有中轉點)&#xff1a;2個鄰接矩陣A和path&#xff0c;第一個是沒有中轉點的2個頂點之間的最短路徑…

uniapp|實現多端圖片上傳、拍照上傳自定義插入水印內容及拖拽自定義水印位置,實現水印相機、圖片下載保存等功能

本文以基礎視角,詳細講解如何在uni-app中實現圖片上傳→水印動態編輯→圖片下載的全流程功能。 目錄 引言應用場景分析(社交媒體、內容保護、企業素材管理等)uniapp跨平臺開發優勢核心功能實現?圖片上傳模塊多來源支持:相冊選擇(`uni.chooseImage`)與拍照(`sourceType:…

2021年認證杯SPSSPRO杯數學建模B題(第二階段)依巴谷星表中的畢星團求解全過程文檔及程序

2021年認證杯SPSSPRO杯數學建模 B題 依巴谷星表中的畢星團 原題再現&#xff1a; 依巴谷衛星&#xff08;High Precision Parallax Collecting Satellite&#xff0c;縮寫為 Hip-parcos&#xff09;&#xff0c;全稱為“依巴谷高精度視差測量衛星”&#xff0c;是歐洲空間局發…

行為型:解釋器模式

目錄 1、核心思想 2、實現方式 2.1 模式結構 2.2 實現案例 3、優缺點分析 4、適用場景 5、注意事項 1、核心思想 目的&#xff1a;針對某種語言并基于其語法特征創建一系列的表達式類&#xff08;包括終極表達式與非終極表達式&#xff09;?&#xff0c;利用樹結構模式…

Redis分布式緩存核心架構全解析:持久化、高可用與分片實戰

一、持久化機制&#xff1a;數據安全雙引擎 1.1 RDB與AOF的架構設計 Redis通過RDB&#xff08;快照持久化&#xff09;和AOF&#xff08;日志持久化&#xff09;兩大機制實現數據持久化。 ? RDB架構&#xff1a;采用COW&#xff08;寫時復制&#xff09;技術&#xff0c;主進程…

換臉視頻FaceFusion3.1.0-附整合包

2025版最強換臉軟件FaceFusion來了&#xff08;附整合包&#xff09;超變態的換臉教程 2025版最強換臉軟件FaceFusion來了&#xff08;附整合包&#xff09;超變態的換臉教程 整合包地址&#xff1a; 「Facefusion_V3.1.0」 鏈接&#xff1a;https://pan.quark.cn/s/f71601a920…

論文閱讀筆記——Step1X-Edit: A Practical Framework for General Image Editing

Step1X-Edit 論文 當前圖像編輯數據集規模小&#xff0c;質量差&#xff0c;由此構建了如下數據構造管線。 高質量三元組數據&#xff08;源圖像、編輯指令、目標圖像&#xff09;。 主體添加與移除&#xff1a;使用 Florence-2 對專有數據集標注&#xff0c;然后使用 SAM2 進…

使用Python在PyCharm中進行交通工程數據分析的完整流程,包括數據清洗、挖掘、關聯、可視化和應用整合等各個階段

交通工程領域數據分析流程 下面我將詳細介紹使用Python在PyCharm中進行交通工程數據分析的完整流程,包括數據清洗、挖掘、關聯、可視化和應用整合等各個階段。 1. 數據準備與清洗 1.1 導入必要庫 import pandas as pd import numpy as np import matplotlib.pyplot as plt…

《軟件工程》第 2 章 -UML 與 RUP 統一過程

在軟件工程領域&#xff0c;UML&#xff08;統一建模語言&#xff09;與 RUP&#xff08;統一過程&#xff09;是進行面向對象軟件開發的重要工具和方法。接下來&#xff0c;我們將深入探討第 2 章的內容&#xff0c;通過案例和代碼&#xff0c;幫助大家理解和掌握相關知識。 …

Vue收集表單數據

在 Web 開發中&#xff0c;表單是用戶與系統交互的重要方式。無論是注冊、登錄、提交評論還是其他操作&#xff0c;都需要通過表單獲取用戶輸入的數據。Vue.js 提供了強大的響應式系統和指令&#xff0c;使得表單數據的收集變得簡單而高效。本文將詳細介紹如何在 Vue 中實現表單…

R基于多元線性回歸模型實現汽車燃油效率預測及SHAP值解釋項目實戰

說明&#xff1a;這是一個機器學習實戰項目&#xff08;附帶數據代碼文檔視頻講解&#xff09;&#xff0c;如需數據代碼文檔視頻講解可以直接到文章最后關注獲取。 1.項目背景 在全球環保意識日益增強和技術進步的推動下&#xff0c;汽車燃油效率成為了汽車行業關注的核心指標…

解決Window10上IP映射重啟失效的問題

問題 在實際網絡搭建過程中&#xff0c;大家有可能會遇到在局域網范圍內&#xff0c;在自己本機上搭建一個網站或者應用時&#xff0c;其他設備通過本機的IP地址無法訪問的問題,這個問題可以通過設置IP映射來解決&#xff0c;但是通過netsh interface命令設置的IP映射&#xf…