[TOC]
### **Go 如何實現原子操作?**
原子操作即是進行過程中不能被中斷的操作,針對某個值的原子操作在被進行 的過程中,CPU 絕不會再去進行其他的針對該值的操作。
Go 語言的標準庫代碼包 sync/atomic 提供了原子的讀取(Load 為前綴的函數)或寫入(Store 為前綴的函數)某個值
### **原子操作與互斥鎖的區別**
1)、互斥鎖是一種數據結構,用來讓一個線程執行程序的關鍵部分,完成互斥的多個操作。
2)、原子操作是針對某個值的單個互斥操作。
### **Mutex 幾種狀態**

* mutexLocked — 表示互斥鎖的鎖定狀態;
* mutexWoken — 表示是否有協程被喚醒;
* mutexStarving — 當前的互斥鎖進入饑餓狀態;
* waitersCount — 當前互斥鎖上等待的 Goroutine 個數;
### **饑餓模式和正常模式**
#### **正常模式(非公平鎖)**
正常模式下waiter 都是進入先入先出隊列,被喚醒的 waiter 并不會直接持有鎖,而是要和新來的 goroutine 進行競爭。新來的 goroutine 有先天的優勢,它們正在 CPU 中運行,可能它們的數量還不少,所以,在高并發情況下,被喚醒的 waiter 可能比較悲劇地獲取不到鎖,這時,它會被插入到隊列的前面。
##### 正常鎖-->饑餓鎖觸發條件:
> 如果 waiter 獲取不到鎖的時間超過閾值 1 毫秒,那么,這個 Mutex 就進入到了饑餓模式。
#### **饑餓模式(公平鎖)**
Mutex 的擁有者將直接把鎖交給隊列最前面的 waiter。新來的 goroutine不會嘗試獲取鎖,即使看起來鎖沒有被持有,它也不會去搶,也不會 spin,它會乖乖地加入到等待隊列的尾部。
##### 饑餓-->正常 轉換:
> * 此 waiter 已經是隊列中的最后一個 waiter 了,沒有其它的等待鎖的 goroutine 了;
> * 此 waiter 的等待時間小于 1 毫秒。
### **WaitGroup 用法**
一個 WaitGroup 對象可以等待一組協程結束。使用方法是:
1. main 協程通過調用 wg.Add(delta int) 設置 worker 協程的個數,然后創 建 worker 協程;
2.worker 協程執行結束以后,都要調用 wg.Done();
3.main 協程調用 wg.Wait() 且被 block,直到所有 worker 協程全部執行結束 后返回。
### **WaitGroup 實現原理**
1、WaitGroup 主要維護了 2 個計數器,一個是請求計數器 v,一個是等待計數 器 w,二者組成一個 64bit 的值,請求計數器占高 32bit,等待計數器占低 32bit。
2、每次 Add 執行,請求計數器 v 加 1,Done 方法執行,等待計數器減 1,v 為 0 時通過信號量喚醒 Wait()。
### **什么是 sync.Once**
1. Once 可以用來執行且僅僅執行一次動作,常常用于單例對象的初始化場 景。
2. Once 常常用來初始化單例資源,或者并發訪問只需初始化一次的共享資 源,或者在測試的時候初始化一次測試資源。 3.sync.Once 只暴露了一個方法 Do,你可以多次調用 Do 方法,但是只有第 一次調用 Do 方法時 f 參數才會執行,這里的 f 是一個無參數無返回值 的函數。
### **goroutine 的自旋占用資源如何解決?**
自旋鎖是指當一個線程在獲取鎖的時候,如果鎖已經被其他線程獲取,那么該線程將循環等待,然后不斷地判斷是否能夠被成功獲取,直到獲取到鎖才會退出循環。
> 自旋條件
1. 鎖已被占用,并且鎖不處于饑餓模式。
2. 積累的自旋次數小于最大自旋次數(active\_spin=4)。
3. CPU 核數大于 1。
4. 有空閑的 P。
5. 當前 Goroutine 所掛載的 P 下,本地待運行隊列為空。
mutex 會讓當前的 goroutine 去空轉 CPU,在空轉完后再次調用 CAS 方法去嘗試性的占有鎖資源,直到不滿足自旋條件,則最終會加入到等待隊列里。
### **sync.Pool 有什么用**
對于很多需要重復分配、回收內存的地方,sync.Pool 是一個很好的選擇。頻 繁地分配、回收內存會給 GC 帶來一定的負擔,嚴重的時候會引起 CPU 的毛 刺。而 sync.Pool 可以將暫時將不用的對象緩存起來,待下次需要的時候直 接使用,不用再次經過內存分配,復用對象的內存,減輕 GC 的壓力,提升系 統的性能。
### **RWMutex 實現**
通過記錄 readerCount 讀鎖的數量來進行控制,當有一個寫鎖的時候,會將讀 鎖數量設置為負數 1<<30。目的是讓新進入的讀鎖等待之前的寫鎖釋放通知讀 鎖。同樣的當有寫鎖進行搶占時,也會等待之前的讀鎖都釋放完畢,才會開始 21 進行后續的操作。 而等寫鎖釋放完之后,會將值重新加上 1<<30, 并通知剛才 新進入的讀鎖(rw.readerSem),兩者互相限制。
### **RWMutex 注意事項**
1. RWMutex 是單寫多讀鎖,該鎖可以加多個讀鎖或者一個寫鎖
2. 讀鎖占用的情況下會阻止寫,不會阻止讀,多個 Goroutine 可以同時獲取 讀鎖
3. 寫鎖會阻止其他 Goroutine(無論讀和寫)進來,整個鎖由該 Goroutine 獨占
4. 適用于讀多寫少的場景
5. RWMutex 類型變量的零值是一個未鎖定狀態的互斥鎖
6. RWMutex 在首次被使用之后就不能再被拷貝
7. RWMutex 的讀鎖或寫鎖在未鎖定狀態,解鎖操作都會引發 panic
8. RWMutex 的一個寫鎖去鎖定臨界區的共享資源,如果臨界區的共享資源已 被(讀鎖或寫鎖)鎖定,這個寫鎖操作的 goroutine 將被阻塞直到解鎖
9. RWMutex 的讀鎖不要用于遞歸調用,比較容易產生死鎖
10. RWMutex 的鎖定狀態與特定的 goroutine 沒有關聯。一個 goroutine 可 以 RLock(Lock),另一個 goroutine 可以 RUnlock(Unlock)
11. 寫鎖被解鎖后,所有因操作鎖定讀鎖而被阻塞的 goroutine 會被喚醒,并 都可以成功鎖定讀鎖
12. 讀鎖被解鎖后,在沒有被其他讀鎖鎖定的前提下,所有因操作鎖定寫鎖而 被阻塞的 Goroutine,其中等待時間最長的一個 Goroutine 會被喚醒
- Go準備工作
- 依賴管理
- Go基礎
- 1、變量和常量
- 2、基本數據類型
- 3、運算符
- 4、流程控制
- 5、數組
- 數組聲明和初始化
- 遍歷
- 數組是值類型
- 6、切片
- 定義
- slice其他內容
- 7、map
- 8、函數
- 函數基礎
- 函數進階
- 9、指針
- 10、結構體
- 類型別名和自定義類型
- 結構體
- 11、接口
- 12、反射
- 13、并發
- 14、網絡編程
- 15、單元測試
- Go常用庫/包
- Context
- time
- strings/strconv
- file
- http
- Go常用第三方包
- Go優化
- Go問題排查
- Go框架
- 基礎知識點的思考
- 面試題
- 八股文
- 操作系統
- 整理一份資料
- interface
- array
- slice
- map
- MUTEX
- RWMUTEX
- Channel
- waitGroup
- context
- reflect
- gc
- GMP和CSP
- Select
- Docker
- 基本命令
- dockerfile
- docker-compose
- rpc和grpc
- consul和etcd
- ETCD
- consul
- gin
- 一些小點
- 樹
- K8s
- ES
- pprof
- mycat
- nginx
- 整理后的面試題
- 基礎
- Map
- Chan
- GC
- GMP
- 并發
- 內存
- 算法
- docker