golang 互斥锁 读写锁
一 为什么加锁?
Go是多线程的,存在多个goroutine同时操作一个资源(临界区),这种情况会发生竞态问题(数据竞态)。类比火车上的卫生间被车厢里的人竞争。
这里注意一点:
go里的锁,并非说给火车的卫生间设置一个门锁,而是给各个乘客发放一个对讲机。乘客先询问对讲机能否使用卫生间,若通过则直接去,不通过则一直等待,知道对讲机另一边通过了。
所以这样我们就可以理解为,倘若乘客x未被发放对讲机,那么他要是用卫生间,则直接前去就好。
那么这样有个问题,当x使用卫生间的时候,其他有对讲机并通过的乘客前往,这就造成了竞态问题。如何解决?---------------------> 很明显,不要遗漏给x发放对讲机!
(当然,另一种解决方案就是设置一个门锁,但是golang的机制不是这样的,这点很关键)
二 sync.Mutex互斥锁
互斥锁能够保证同一时间有且只有一个goroutine进入临界区,其他的goroutine则在等待锁;
当互斥锁释放后,等待的goroutine才可以获取锁进入临界区,多个goroutine同时等待一个锁时,唤醒的策略是随机的。
(我一加锁,其他人啥都别干,等着吧)
func main(){ var lock sync.Mutex go func() { fmt.Println("乘客1询问对讲机能否使用卫生间 at "+ time.Now().String()) lock.Lock() defer lock.Unlock() fmt.Println("乘客1开始 at "+ time.Now().String()) time.Sleep(time.Second * 2) fmt.Println("乘客1结束 at "+ time.Now().String()) }() time.Sleep(time.Second/10) go func() { fmt.Println("乘客2询问对讲机能否使用卫生间 at "+ time.Now().String()) lock.Lock() defer lock.Unlock() fmt.Println("乘客2开始 at "+ time.Now().String()) time.Sleep(time.Second * 2) fmt.Println("乘客2结束 at "+ time.Now().String()) }() go func() { fmt.Println("乘客3(无对讲机)直接使用 "+ time.Now().String()) time.Sleep(time.Second * 2) fmt.Println("乘客3(无对讲机)结束 "+ time.Now().String()) }() //fmt.Println("main run "+ time.Now().String()) time.Sleep(time.Second * 15) }
我们看到 31s 的时候,1 2 3 同时想使用卫生间, 1先获取到对讲机的权限,开始使用,2就只能等待,而3没有对讲机,那么他就直接使用了。
33s的时候, 1 和 3 使用完毕,2得到了对讲机的权限,开始使用。
35s的时候,2 使用结束。
(要处理3和1同时使用的问题,就要给3也加个对讲机,也就是代码中上个锁)
三 sync.RWMutex读写互斥锁
读写锁分为两种:读锁和写锁。
当一个goroutine获取读锁之后,其他的goroutine如果是获取读锁会继续获得锁,如果是获取写锁就会等待;
当一个goroutine获取写锁之后,其他的goroutine无论是获取读锁还是写锁都会等待。
- 读锁RLock() ,我开始读了,你们也可以来读,但是谁都别写
- 写锁Lock() ,我要写了,所有人注意,别做任何操作
func main(){ var rwLock sync.RWMutex fmt.Println("begin " + time.Now().String()) // 获取写锁 for i := 0 ; i < 5; i++{ go func(i int) { rwLock.Lock() defer rwLock.Unlock() fmt.Println("write func " + strconv.Itoa(i) +" get wlock at " + time.Now().String()) time.Sleep(time.Second) }(i) }// 获取读锁 for i := 0 ; i < 5 ;i ++{ go func(i int) { rwLock.RLock() defer rwLock.RUnlock() fmt.Println("read func " + strconv.Itoa(i) +" get rlock at " + time.Now().String()) time.Sleep(time.Second) }(i) } //散人 for i := 0 ; i < 5 ;i ++{ go func(i int) { fmt.Println("nolocker func " + strconv.Itoa(i) +" get rlock at " + time.Now().String()) time.Sleep(time.Second) }(i) } // 保证所有的 goroutine 执行结束 time.Sleep(time.Second * 10) }
我们看到 58s 的时候,write0 获取了写锁(其他人啥事别干),此时read动弹不了,不过散人nolocker那五位牛人可以把事情做完(因为他没有对讲机)
59s的时候,write0 的事情干完了,对讲机通知read0开始干活,1干活的时候上了读锁(其他reader可以干活,writer就别动弹了),所以59s的时候,read0~read4开始干活
60s时,read0~read4全都干完活,释放了他们的读锁,那么write锁可以动弹了,对讲机随机喊来了write1来干活,write1加了写锁,直到干完活释放
61s也就是1s的时候write2开始干活,依次执行,所有write干完
本篇到此结束!!!
原文地址:https://www.cnblogs.com/bushuwei/p/15161668.html
- 国内环境下前端网页开发的几个“中国特色”代码
- 从源码的角度再看 React JS 中的 setState
- Sass 与Compass 在WordPress 主题开发中的运用
- Python爬虫Scrapy入门看这篇就够了
- Clef:为你的WordPress 站点添加两步验证
- JavaScript 基础(六) 数组方法 闭包
- 【译】WordPress 中的50个过滤器(4):第21-30个过滤器
- 【译】WordPress 中的50个过滤器(3):第11-20个过滤器
- 【译】WordPress 中的50个过滤器(2):先介绍10个过滤器
- 【译】WordPress 中的50个过滤器(1):何为过滤器?
- 哪种芯片架构将成为人工智能时代的开路先锋
- 算法系列(三)
- Facebook、Google、Amazon 是如何高效开会的
- 算法系列(二)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法