> 在并发的情况下,多个线程或协程同时去修改同一变量。使用锁能保证在某一时间点内,只有一个协程或线程修改这一变量
## 互斥锁
> 并发程序中最使用互斥锁方式,当有一个 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 快捷键