目录

  • 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和线程安全的更多相关文章

  1. Go语言学习笔记十一: 切片(slice)

    Go语言学习笔记十一: 切片(slice) 切片这个概念我是从python语言中学到的,当时感觉这个东西真的比较好用.不像java语言写起来就比较繁琐.不过我觉得未来java语法也会支持的. 定义切片 ...

  2. 【原创】go语言学习(十一)package简介

    目录 Go源码组织方式 main函数和main包 编译命令 自定义包 init函数以及执行行顺序 _标识符 Go源码组织方式 1. Go通过package的方式来组织源码 package 包名 注意: ...

  3. Swift5 语言指南(二十一) 嵌套类型

    通常创建枚举以支持特定类或结构的功能.类似地,定义纯粹在更复杂类型的上下文中使用的实用程序类和结构可能是方便的.为此,Swift允许您定义嵌套类型,从而在它们支持的类型定义中嵌套支持枚举,类和结构. ...

  4. Dart语言学习(二) Dart的常量和变量

    1.使用var声明变量,可赋予不同类型的值 Dart是一个强大的脚本类语言,可以不预先定义变量类型 ,自动会类型推导 Dart中定义变量可以通过var关键字可以通过类型来申明变量 var str='t ...

  5. C语言学习 第十一次作业总结

    作业总结 两次的作业,都是和指针有关.从第一次的作业开始,我就多次让同学们思考这个问题:为什么要用指针,为什么在函数的形参中要使用指针.如果能够想明白这2个问题,那么同学们应该会指针的了解就差不多足够 ...

  6. (原创)mybatis学习二,spring和mybatis的融合

    mybatis学习一夯实基础 上文介绍了mybatis的相关知识,这一节主要来介绍mybaits和spring的融合 一,环境搭建 1,jar包下载,下载路径为jar包 2,将包导入到java工程中 ...

  7. R语言学习 第十一篇:日期和时间

    R语言的基础包中提供了三种基本类型用于处理日期和时间,Date用于处理日期,它不包括时间和时区信息:POSIXct/POSIXlt用于处理日期和时间,其中包括了日期.时间和时区信息.R内部在存储日期和 ...

  8. Python3.5 学习二十一

    本节内容概要: 上节回顾及补充知识点: 一.请求周期: URL->路由->函数或类->返回字符串或者模板 Form表单提交: 提交->url-函数或者类中的方法 -....(执 ...

  9. Scala学习二十一——隐式转换和隐式参数

    一.本章要点 隐式转换用于类型之间的转换 必须引入隐式转换,并确保它们可以以单个标识符的形式出现在当前作用域 隐式参数列表会要求指定类型的对象.它们可以从当前作用域中以单个标识符定义的隐式对象的获取, ...

  10. JavaWeb学习 (二十一)————基于Servlet+JSP+JavaBean开发模式的用户登录注册

    一.Servlet+JSP+JavaBean开发模式(MVC)介绍 Servlet+JSP+JavaBean模式(MVC)适合开发复杂的web应用,在这种模式下,servlet负责处理用户请求,jsp ...

随机推荐

  1. python爬虫——简易天气爬取

    通过爬虫,抓取http://www.weather.com.cn的天气信息 功能——输入城市代码,获取当日天气,简单的beautifulsoup和requests实现.(城市代码可百度查询,不全部展示 ...

  2. 【JZOJ5263】分手是祝愿

    Description 请注意本题的数据范围. Input Output Sample Input 2 2 15 19 3 30 40 20 Sample Output 285 2600 Hint 数 ...

  3. [ZJOI2006]物流运输trans

    Description 物流公司要把一批货物从码头A运到码头B.由于货物量比较大,需要n天才能运完.货物运输过程中一般要转停好几个码头.物流公司通常会设计一条固定的运输路线,以便对整个运输过程实施严格 ...

  4. 编译 lame for iOS

    网上找了许多编译lame的教程,结果都是编译失败,多次尝试后发现是编译脚本放错路径了,记录下编译的过程,把编译脚本放到源码文件夹中和修改编译脚本中的目录是关键: 一.首先去Lame官网 http:// ...

  5. Windows系统调用中的现场保存

    Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html Windows系统调用中的现场保存 我们之前介绍过三环进零环的步骤 ...

  6. HeidiSQL、Navicat、mysql命令和source命令导入sql脚本的速度比较

    一.四种导入方式的比较 1.heidisql客户端是一条一条插入的,速度最慢,而且很脆弱比较容易崩溃: 2.mysql命令导入380万记录用时1小时13分(属于前台运行的命令,ctrl+c就可以结束) ...

  7. HMLT clear 属性

    原文 : http://www.zhangxinxu.com/wordpress/2014/06/understand-css-clear-left-right-and-use/ clear 的四个值 ...

  8. Halcon C# 联合编程问题(三)

    因为之前遇到的那个halcon处理的图片要转换成ImageSource的问题,迟迟没有找到好的解决方案, 于是决定直接在wpf中使用halcon提供的HWindowControlWPF,用于显示图片. ...

  9. 图像处理笔记(二十一):halcon在图像处理中的运用

    概要: 分水岭算法做图像分割 二维码识别 稍后将其他几篇笔记全都补充上概要方便查询. 分水岭算法做图像分割 使用距离变换结合分水岭算法实现图像分割,可以用来分割仅通过阈值分割还是有边缘连接在一起的情况 ...

  10. 推荐几个IT交流社区

    博客园,csdn,掘金,StackOverflow(境外),v2ex,开源中国,简书,头条