【原创】go语言学习(二十一)Select和线程安全
目录
- select语义介绍和使用
- 线程安全介绍
- 互斥锁介绍和实战
- 读写锁介绍和实战
- 原子操作介绍
select语义介绍和使用
1、多channel场景
A. 多个channel同时需要读取或写入,怎么办?
B. 串行操作?
package main
import (
"fmt"
"time"
)
func server1(ch chan string) {
time.Sleep(6 * time.Second)
ch <- "from server1"
}
func server2(ch chan string) {
time.Sleep(3 * time.Second)
ch <- "from server2"
}
func main() {
output1 := make(chan string)
output2 := make(chan string)
go server1(output1)
go server2(output2)
s1 := <-output1
fmt.Println(s1)
s2 := <-output2
fmt.Println(s2)
}
2、select登场
A. 同时监听一个或多个channel,直到其中一个channel ready
B. 如果其中多个channel同时ready,随机选择一个进行操作。
C. 语法和switch case有点类似,代码可读性更好。
select {
case s1 := <-output1:
fmt.Println(s1)
case s2 := <-output2:
fmt.Println(s2)
}
package main
import (
"fmt"
"time"
)
func server1(ch chan string) {
time.Sleep(6 * time.Second)
ch <- "from server1"
}
func server2(ch chan string) {
time.Sleep(3 * time.Second)
ch <- "from server2"
}
func main() {
output1 := make(chan string)
output2 := make(chan string)
go server1(output1)
go server2(output2)
select {
case s1 := <-output1:
fmt.Println(s1)
case s2 := <-output2:
fmt.Println(s2)
}
}
3、default分支,当case分支的channel都没有ready的话,执行default
A. 用来判断channel是否满了
B. 用来判断channel是否是空的
package make import (
"fmt"
"time"
) // select 管道参数并行 func server1(ch chan string) {
time.Sleep(time.Second * 6)
ch <- "response from server1"
} func server2(ch chan string){
time.Sleep(time.Second * 3)
ch <- "response from server2"
} func main(){
output1 := make(chan string)
output2 := make(chan string) go server1(output1)
go server2(output2) /*
s1 := <-output1
fmt.Println("s1:", s1) s2 := <-output2
fmt.Println("s2:", s2)
*/
// 管道同时ready,select随机执行
// time.Sleep(time.Second)
select{
case s1 := <-output1:
fmt.Println("s1:", s1)
case s2 := <-output2:
fmt.Println("s2:", s2)
default:
fmt.Println("run default")
}
}
4、select case分支随机策略验证
package main
import (
"fmt"
"time"
)
func server1(ch chan string) {
ch <- "from server1"
}
func server2(ch chan string) {
ch <- "from server2"
}
func main() {
output1 := make(chan string)
output2 := make(chan string)
go server1(output1)
go server2(output2)
time.Sleep(1 * time.Second)
select {
case s1 := <-output1:
fmt.Println(s1)
case s2 := <-output2:
fmt.Println(s2)
}
}
5、empty select
package main
func main() {
// 代码阻塞
// select{}
select{}
}
线程安全介绍
1、现实例子
A. 多个goroutine同时操作一个资源,这个资源又叫临界区
B. 现实生活中的十字路口,通过红路灯实现线程安全
C. 火车上的厕所,通过互斥锁来实现线程安全
2、实际例子, x = x +1
A. 先从内存中取出x的值
B. CPU进行计算,x+1
C. 然后把x+1的结果存储在内存中
互斥锁介绍和实战
1、互斥锁介绍
A. 同时有且只有一个线程进入临界区,其他的线程则在等待锁
B. 当互斥锁释放之后,等待锁的线程才可以获取锁进入临界区
C. 多个线程同时等待同一个锁,唤醒的策略是随机的
package main
import (
"fmt"
"sync"
) //有问题的代码! ! var x = 0
func increment(wg *sync.WaitGroup) {
x = x + 1
wg.Done()
}
func main() {
var w sync.WaitGroup
for i := 0; i < 1000; i++ {
w.Add(1)
go increment(&w)
}
w.Wait()
fmt.Println("final value of x", x)
}
package main
import (
"fmt"
"sync"
)
//使用互斥锁
var x = 0
func increment(wg *sync.WaitGroup, m *sync.Mutex) {
m.Lock()
x = x + 1
m.Unlock()
wg.Done()
}
func main() {
var w sync.WaitGroup
var m sync.Mutex
for i := 0; i < 1000; i++ {
w.Add(1)
go increment(&w, &m)
}
w.Wait()
fmt.Println("final value of x", x)
}
读写锁介绍和实战
1、读写锁使用场景
A. 读多写少的场景
B. 分为两种角色,读锁和写锁
C. 当一个goroutine获取写锁之后,其他的goroutine获取写锁或读锁都会等待读写锁介绍
D. 当一个goroutine获取读锁之后,其他的goroutine获取写锁都会等待, 但其他goroutine获取读锁时,都会继续获得锁.
package main import (
"fmt"
"sync"
"time"
) // 读写锁
var rwlock sync.RWMutex
var x int
var wg sync.WaitGroup func write(){
fmt.Println("wait for rlock")
//获得写锁
rwlock.Lock()
fmt.Println("write lock")
x = x + 1
fmt.Println(10 * time.Second)
fmt.Println("write unlock")
rwlock.Unlock()
wg.Done()
} func read(i int){
//获取一个读锁
rwlock.RLock()
fmt.Printf("goroutine:%d x=%d", i, x)
// time.Sleep(time.Second)
rwlock.RUnlock()
wg.Done()
} func main(){
for i := 0 ; i < 10; i++{
wg.Add(1)
go read(i)
} wg.Add(1)
go write()
wg.Wait()
}
2、读写锁和互斥锁性能比较
package main import (
"fmt"
"sync"
"time"
) // 读写锁 比较 互斥锁
// 读写锁在读多写少的情况下比互斥锁效率高15倍
var rwlock sync.RWMutex
var x int
var wg sync.WaitGroup var mutex sync.Mutex func write(){
for i := 0; i <1000;i++{
//获得写锁
// rwlock.Lock()
mutex.Lock()
x = x + 1
time.Sleep(10 * time.Microsecond)
// rwlock.Unlock()
mutex.Unlock()
wg.Done()
} } func read(i int){
for i := 0; i <10000;i++ {
//获取一个读锁
// rwlock.RLock()
mutex.Lock()
fmt.Printf("goroutine:%d x=%d", i, x)
// time.Sleep(time.Second)
time.Sleep(10 * time.Microsecond)
// rwlock.RUnlock()
mutex.Unlock()
}
wg.Done()
} func main() {
start := time.Now().UnixNano()
wg.Add(1)
go write()
for i := 0; i < 100; i++ {
wg.Add(1)
go read(i)
} wg.Wait()
end := time.Now().UnixNano() cost := (end - start) / 1000 / 1000
fmt.Println("cost:", cost, "ms")
}
原子操作介绍
1、原子操作
A. 加锁代价比较耗时,需要上下文切换
B. 针对基本数据类型,可以使用原子操作保证线程安全
C. 原子操作在用户态就可以完成,因此性能比互斥锁要高
【原创】go语言学习(二十一)Select和线程安全的更多相关文章
- Go语言学习笔记十一: 切片(slice)
Go语言学习笔记十一: 切片(slice) 切片这个概念我是从python语言中学到的,当时感觉这个东西真的比较好用.不像java语言写起来就比较繁琐.不过我觉得未来java语法也会支持的. 定义切片 ...
- 【原创】go语言学习(十一)package简介
目录 Go源码组织方式 main函数和main包 编译命令 自定义包 init函数以及执行行顺序 _标识符 Go源码组织方式 1. Go通过package的方式来组织源码 package 包名 注意: ...
- Swift5 语言指南(二十一) 嵌套类型
通常创建枚举以支持特定类或结构的功能.类似地,定义纯粹在更复杂类型的上下文中使用的实用程序类和结构可能是方便的.为此,Swift允许您定义嵌套类型,从而在它们支持的类型定义中嵌套支持枚举,类和结构. ...
- Dart语言学习(二) Dart的常量和变量
1.使用var声明变量,可赋予不同类型的值 Dart是一个强大的脚本类语言,可以不预先定义变量类型 ,自动会类型推导 Dart中定义变量可以通过var关键字可以通过类型来申明变量 var str='t ...
- C语言学习 第十一次作业总结
作业总结 两次的作业,都是和指针有关.从第一次的作业开始,我就多次让同学们思考这个问题:为什么要用指针,为什么在函数的形参中要使用指针.如果能够想明白这2个问题,那么同学们应该会指针的了解就差不多足够 ...
- (原创)mybatis学习二,spring和mybatis的融合
mybatis学习一夯实基础 上文介绍了mybatis的相关知识,这一节主要来介绍mybaits和spring的融合 一,环境搭建 1,jar包下载,下载路径为jar包 2,将包导入到java工程中 ...
- R语言学习 第十一篇:日期和时间
R语言的基础包中提供了三种基本类型用于处理日期和时间,Date用于处理日期,它不包括时间和时区信息:POSIXct/POSIXlt用于处理日期和时间,其中包括了日期.时间和时区信息.R内部在存储日期和 ...
- Python3.5 学习二十一
本节内容概要: 上节回顾及补充知识点: 一.请求周期: URL->路由->函数或类->返回字符串或者模板 Form表单提交: 提交->url-函数或者类中的方法 -....(执 ...
- Scala学习二十一——隐式转换和隐式参数
一.本章要点 隐式转换用于类型之间的转换 必须引入隐式转换,并确保它们可以以单个标识符的形式出现在当前作用域 隐式参数列表会要求指定类型的对象.它们可以从当前作用域中以单个标识符定义的隐式对象的获取, ...
- JavaWeb学习 (二十一)————基于Servlet+JSP+JavaBean开发模式的用户登录注册
一.Servlet+JSP+JavaBean开发模式(MVC)介绍 Servlet+JSP+JavaBean模式(MVC)适合开发复杂的web应用,在这种模式下,servlet负责处理用户请求,jsp ...
随机推荐
- 深入Dapper.NET源码 (文长)
目录 前言.目录.安装环境 Dynamic Query 原理 Part1 Dynamic Query 原理 Part2 Strongly Typed Mapping 原理 Part1 : ADO.NE ...
- jquery复习日记(1)
jquery封装了JavaScript常用的功能代码,提供一种简便的JavaScript设计模式,优化HTML文档操作.事件处理.动画设计和Ajax交互. 核心关键字: 链式.多功能.高效灵活 1 ...
- [PHP] php, apache, VS Code安装与配置
1. 下载
- MySQL 特性:Double Write
1.什么是double write 首先要明白double write这个特性是针对谁的,日志or脏数据? 明白的是脏数据,是内存中修改后的数据页,这些数据页修改后和磁盘上存储的原数据不一致了,称为脏 ...
- .Net Core上传文件到服务器
/// <summary> /// 上传文件 /// </summary> /// <returns></returns> [HttpPost(&quo ...
- Android通过外部浏览器调用微信H5支付,Android+PHP详解
看了好多关于讲解微信H5支付开发的文章,大多数都是通过微信内部浏览器来调用支付接口(其实就是公众号支付),可能是因为H5支付接口刚开放不久吧. 微信官方体验链接:http://wxpay.wxutil ...
- spring配置文件默认名称及位置,ContextLoaderListener监听器作用
spring在web.xml中的配置 由于spring需要启动容器才能为其他框架提供服务,而web应用程序的入口是由web服务器控制的,因此无法在main()方法中通过创建ClassPathXmlAp ...
- jquery经常用到的代码段
1.1jquery实现手风琴效果 <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"&g ...
- Linux 命令之 chmod
命令格式 chmod有以下三种格式 chmod [-cfvR] MODE[,MODE]... FILE... chmod [-cfvR] OCTAL-MODE FILE... chmod [-cfvR ...
- python 报错TypeError: 'range' object does not support item assignment,解决方法
贴问题 nums = range(5)#range is a built-in function that creates a list of integers print(nums)#prints ...