成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Go運行時中的 Mutex

開發 前端
我在極客時間上開了一門面向中高級Go程序員的課程:Go 并發編程實戰課,有讀者問Go channel中的實現中使用了mutex,這個mutex和標準庫中的Mutex有什么不同?正好在百度廠內分享Go相關課程中有同事也提出了相同的問題,所以我專門寫一篇文章介紹一下。

我在極客時間上開了一門面向中高級Go程序員的課程:Go 并發編程實戰課,有讀者問Go channel中的實現中使用了mutex,這個mutex和標準庫中的Mutex有什么不同?正好在百度廠內分享Go相關課程中有同事也提出了相同的問題,所以我專門寫一篇文章介紹一下。

[[356059]]

sync.Mutex是一個high level的同步原語,是為廣大的Go開發者開發應用程序提供的一種數據結構,現在它的內部實現邏輯比較復雜了,包含spin和饑餓處理等邏輯,它底層使用了運行時的low level的一些函數和atomic的一些方法。

而運行時中的mutex是為運行時內部使用互斥鎖而提供的一個同步原語,它提供了spin和等待隊列,并沒有去解決饑餓狀態,而且它的實現和sync.Mutex的實現也是不一樣的。它并沒有以方法的方式提供Lock/Unlock,而是提供lock/unlock函數實現請求鎖和釋放鎖。

Dan Scales 今年年初的時候又為運行時的鎖增加了static locking rank的功能。他為運行時的架構無關的鎖( architecture-independent locks)定義了rank,并且又定義了一些運行時的鎖的偏序(此鎖之前允許持有哪些鎖)。這是運行時鎖的一個巨大改變,但是很遺憾并沒有一篇設計文檔詳細去描述這個功能的設計,你可以通過提交的comment(#0a820007)和代碼中的注釋去了解runtime內部鎖的代碼變化。

本質上來說,這個功能用來檢查鎖的順序是不是按照文檔設計的順序執行的,如果有違反設定的順序,就有可能死鎖發生。因為缺乏準確的文檔說明,并且這個功能主要是用來檢查運行時鎖的執行順序的,所以在本文中我把這一段邏輯抹去不介紹了。實際Go運行時要開始這個檢查的話,你需要設置變量GOEXPERIMENT=staticlockranking。

那么接下來我們看看運行時的mutex的數據結構的定義以及lock/unlock的實現。

運行時mutex數據結構

運行時的mutex數據結構很簡單,如下所示,定義在runtime2.go中:

 

  1. type mutex struct { 
  2.     lockRankStruct 
  3.     // Futex-based impl treats it as uint32 key
  4.     // while sema-based impl as M* waitm. 
  5.     // Used to be a union, but unions break precise GC. 
  6.     key uintptr 

如果不啟用lock ranking,其實lockRankStruct就是一個空結構:

 

  1. type lockRankStruct struct { 

那么對于運行時的mutex,最重要的就是key字段了。這個字段針對不同的架構有不同的含義。

對于dragonfly、freebsd、linux架構,mutex會使用基于Futex的實現, key就是一個uint32的值。 Linux提供的Futex(Fast user-space mutexes)用來構建用戶空間的鎖和信號量。Go 運行時封裝了兩個方法,用來sleep和喚醒當前線程:

  • futexsleep(addr uint32, val uint32, ns int64):原子操作`if addr == val { sleep }`。
  • futexwakeup(addr *uint32, cnt uint32):喚醒地址addr上的線程最多cnt次。

對于其他的架構,比如aix、darwin、netbsd、openbsd、plan9、solaris、windows,mutex會使用基于sema的實現,key就是M* waitm。Go 運行時封裝了三個方法,用來創建信號量和sleep/wakeup:

  • func semacreate(mp *m):創建信號量
  • func semasleep(ns int64) int32: 請求信號量,請求不到會休眠一段時間
  • func semawakeup(mp *m):喚醒mp

基于這兩種實現,分別有不同的lock和unlock方法的實現,主要邏輯都是類似的,所以接下來我們只看基于Futex的lock/unlock。

請求鎖lock

如果不使用lock ranking特性,lock的邏輯主要是由lock2實現的。

 

  1. func lock(l *mutex) { 
  2.     lockWithRank(l, getLockRank(l)) 
  3. func lockWithRank(l *mutex, rank lockRank) { 
  4.     lock2(l) 
  5. func lock2(l *mutex) { 
  6.     // 得到g對象 
  7.     gp := getg() 
  8.     // g綁定的m對象的lock計數加1 
  9.     if gp.m.locks < 0 { 
  10.         throw("runtime·lock: lock count"
  11.     } 
  12.     gp.m.locks++ 
  13.     // 如果有幸運光環,原來鎖沒有被持有,一把就獲取到了鎖,就快速返回了 
  14.     v := atomic.Xchg(key32(&l.key), mutex_locked) 
  15.     if v == mutex_unlocked { 
  16.         return 
  17.     } 
  18.     // 否則原來的可能是MUTEX_LOCKED或者MUTEX_SLEEPING 
  19.     wait := v 
  20.     // 單核不進行spin,多核CPU情況下會嘗試spin 
  21.     spin := 0 
  22.     if ncpu > 1 { 
  23.         spin = active_spin 
  24.     } 
  25.      
  26.     for { 
  27.         // 嘗試spin,如果鎖已經釋放,嘗試搶鎖 
  28.         for i := 0; i < spin; i++ { 
  29.             for l.key == mutex_unlocked { 
  30.                 if atomic.Cas(key32(&l.key), mutex_unlocked, wait) { 
  31.                     return 
  32.                 } 
  33.             } 
  34.             // PAUSE 
  35.             procyield(active_spin_cnt) 
  36.         } 
  37.         // 再嘗試搶鎖, rescheduling. 
  38.         for i := 0; i < passive_spin; i++ { 
  39.             for l.key == mutex_unlocked { 
  40.                 if atomic.Cas(key32(&l.key), mutex_unlocked, wait) { 
  41.                     return 
  42.                 } 
  43.             } 
  44.             osyield() 
  45.         } 
  46.         // 再嘗試搶鎖,并把key設置為mutex_sleeping,如果搶鎖成功,返回 
  47.         v = atomic.Xchg(key32(&l.key), mutex_sleeping) 
  48.         if v == mutex_unlocked { 
  49.             return 
  50.         } 
  51.          
  52.         // 否則sleep等待 
  53.         wait = mutex_sleeping 
  54.         futexsleep(key32(&l.key), mutex_sleeping, -1) 
  55.     } 

unlock

如果不使用lock ranking特性,unlock的邏輯主要是由unlock2實現的。

 

  1. func unlock(l *mutex) { 
  2.     unlockWithRank(l) 
  3. func unlockWithRank(l *mutex) { 
  4.     unlock2(l) 
  5. func unlock2(l *mutex) { 
  6.     // 將key的值設置為mutex_unlocked 
  7.     v := atomic.Xchg(key32(&l.key), mutex_unlocked) 
  8.     if v == mutex_unlocked { 
  9.         throw("unlock of unlocked lock"
  10.     } 
  11.     // 如果原來有線程在sleep,喚醒它 
  12.     if v == mutex_sleeping { 
  13.         futexwakeup(key32(&l.key), 1) 
  14.     } 
  15.     //得到當前的goroutine以及和它關聯的m,將鎖的計數減1 
  16.     gp := getg() 
  17.     gp.m.locks-- 
  18.     if gp.m.locks < 0 { 
  19.         throw("runtime·unlock: lock count"
  20.     } 
  21.     if gp.m.locks == 0 && gp.preempt { // restore the preemption request in case we've cleared it in newstack 
  22.         gp.stackguard0 = stackPreempt 
  23.     } 

總體來說,運行時的mutex邏輯還不太復雜,主要是需要處理不同的架構的實現,它休眠喚醒的對象是m,而sync.Mutex休眠喚醒的對象是g。

責任編輯:未麗燕 來源: 大道至簡
相關推薦

2015-07-20 15:44:46

Swift框架MJExtension反射

2014-09-02 10:39:53

Go語言C語言

2025-03-28 01:00:00

Go語言版本

2024-03-21 09:15:58

JS運行的JavaScrip

2017-12-18 16:50:26

Gobug編譯

2019-07-12 09:30:12

DashboardDockerDNS

2021-09-11 15:38:23

容器運行鏡像開放

2023-08-27 21:07:02

2023-08-21 09:37:57

MySQL工具MariaDB

2024-01-29 08:07:42

FlinkYARN架構

2013-11-26 16:49:55

Android開發運行時KitKat

2021-08-18 08:32:09

代碼運行時間示波器

2023-07-28 10:42:43

2023-01-03 09:10:21

2024-03-20 10:46:00

云原生容器

2022-01-19 08:50:53

設備樹Linux文件系統

2023-02-12 12:00:57

2023-08-29 08:20:35

Kubernete跨云容器

2021-08-27 00:21:19

JSJust源碼

2022-12-30 08:08:30

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧美啪啪网站 | av影音资源 | 在线免费观看黄色 | 992tv人人草| 可以在线观看av的网站 | 亚洲国产精品一区在线观看 | 亚洲美女网站 | 伊人网站在线 | 99精品国产一区二区青青牛奶 | 不卡一区 | 精品久久久久久一区二区 | 成人午夜 | 国产精品视频久久久久 | 91精品一区 | ww 255hh 在线观看 | 日韩欧美国产精品一区二区 | 日韩一区二区视频 | 午夜色婷婷 | 国产精品中文字幕在线播放 | 亚洲成人一区二区三区 | 久久久爽爽爽美女图片 | 欧美一区二区在线观看视频 | 亚洲国产区 | 蜜月aⅴ国产精品 | 亚洲高清视频一区二区 | 国产精品一区久久久久 | 日韩av高清在线 | 一区二区三区精品视频 | 久久四虎| 亚洲成人午夜电影 | 日本一区二区不卡视频 | 欧美日韩一区二区在线 | 成人在线激情 | 一区二区成人 | 性色av一区 | 91 在线 | 91精品久久久久久久久久小网站 | 亚洲一区二区三区在线播放 | 欧美激情精品久久久久久 | av网站免费 | 中文字幕一区二区在线观看 |