> 在并發的情況下,多個線程或協程同時去修改同一變量。使用鎖能保證在某一時間點內,只有一個協程或線程修改這一變量
## 互斥鎖
> 并發程序中最使用互斥鎖方式,當有一個 goroutine 獲得互斥鎖,其他的互斥鎖都將阻塞等待其解鎖后才能執行
~~~
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var mutex sync.Mutex
count := 0
for i := 0; i < 50; i++ {
go func() {
mutex.Lock()
//defer mutex.Unlock()
count += 1
mutex.Unlock()
}()
}
// 主協程停留一下,好讓子協程都計算完
time.Sleep(time.Second)
fmt.Println("the count is : ", count)
}
~~~
## 讀寫鎖
> 當有一個 goroutine 獲得寫鎖定,其它無論是讀鎖定還是寫鎖定都將阻塞直到寫解鎖;
> 當有一個 goroutine 獲得讀鎖定,其它讀鎖定仍然可以繼續;當有一個或任意多個讀鎖定,寫鎖定將等待所有讀鎖定解鎖之后才能夠進行寫鎖定。所以說這里的讀鎖定(RLock)目的其實是告訴寫鎖定:有很多人正在讀取數據,你給我站一邊去,等它們讀(讀解鎖)完你再來寫(寫鎖定)
~~~
# 寫操作的鎖定和解鎖
* func (*RWMutex) Lock
* func (*RWMutex) Unlock
# 讀操作的鎖定和解鎖
* func (*RWMutex) Rlock
* func (*RWMutex) RUnlock
~~~
> 示例
~~~
package main
import (
"fmt"
"math/rand"
"sync"
)
var count int
var rw sync.RWMutex
func main() {
ch := make(chan struct{}, 10)
for i := 0; i < 5; i++ {
go read(i, ch)
}
for i := 0; i < 5; i++ {
go write(i, ch)
}
for i := 0; i < 10; i++ {
<-ch
}
}
func read(n int, ch chan struct{}) {
rw.RLock()
fmt.Printf("goroutine %d 進入讀操作...\n", n)
v := count
fmt.Printf("goroutine %d 讀取結束,值為:%d\n", n, v)
rw.RUnlock()
ch <- struct{}{}
}
func write(n int, ch chan struct{}) {
rw.Lock()
fmt.Printf("goroutine %d 進入寫操作...\n", n)
v := rand.Intn(1000)
count = v
fmt.Printf("goroutine %d 寫入結束,新值為:%d\n", n, v)
rw.Unlock()
ch <- struct{}{}
}
~~~
> 結果
> 從該例子中,我們可以直觀的看到,當goroutine 獲得寫鎖定,其他的讀或寫鎖定都將阻塞,而goroutine 獲得讀鎖定時,其他的讀鎖定照常執行,只有寫鎖定被阻塞
~~~
goroutine 4 進入寫操作...
goroutine 4 寫入結束,新值為:81
goroutine 4 進入讀操作...
goroutine 4 讀取結束,值為:81
goroutine 2 進入讀操作...
goroutine 2 讀取結束,值為:81
goroutine 0 進入讀操作...
goroutine 0 讀取結束,值為:81
goroutine 3 進入讀操作...
goroutine 3 讀取結束,值為:81
goroutine 1 進入讀操作...
goroutine 1 讀取結束,值為:81
goroutine 1 進入寫操作...
goroutine 1 寫入結束,新值為:887
goroutine 2 進入寫操作...
goroutine 2 寫入結束,新值為:847
goroutine 3 進入寫操作...
goroutine 3 寫入結束,新值為:59
goroutine 0 進入寫操作...
goroutine 0 寫入結束,新值為:81
~~~
- 基礎知識
- 開發環境
- 包名規則
- 包初始化 (init)
- 基礎數據類型
- 基礎類型轉換
- 格式化輸出
- go指針
- 流程控制語句
- 函數定義
- 匿名函數
- 數組和切片
- map集合
- 結構體
- Interface接口
- 日期處理
- 數學計算
- 正則表達式
- 協程 (并發處理)
- channel
- waitgroup
- mutex (鎖機制)
- websocket
- protobuf
- Redis
- 錯誤處理
- 打包程序
- NSQ消息隊列
- 單元測試
- beego
- 安裝入門
- Gin
- 快速入門
- 路由與控制器
- 處理請求參數
- 表單驗證
- 處理響應結果
- 渲染HTML模版
- 訪問靜態文件
- Gin中間件
- Cookie處理
- Session處理
- Gin上傳文件
- swagger
- pprof性能測試
- GORM
- 入門教程
- 模型定義
- 數據庫連接
- 插入數據
- 查詢數據
- 更新數據
- 刪除數據
- 事務處理
- 關聯查詢
- 屬于 (BELONG TO)
- 一對一 (Has One)
- 一對多 (Has Many)
- 多對多 (Many to Many)
- 預加載 (Preloading)
- 錯誤處理
- 第三方常用插件
- viper 讀取配置文件
- zap 高性能日志
- Nginx代理配置
- Goland 快捷鍵