Go中天然的支持并发,Go允许使用go语句开启一个新的运行期线程,即 goroutine,以一个不同的、新创建的goroutine来执行一个函数。同一个程序中的所有goroutine共享同一个地址空间。

Goroutine非常轻量,除了为之分配的栈空间,其所占用的内存空间微乎其微。并且其栈空间在开始时非常小,之后随着堆存储空间的按需分配或释放而变化。内部实现上,goroutine会在多个操作系统线程上多路复用。如果一个goroutine阻塞了一个操作系统线程,例如:等待输入,这个线程上的其他goroutine就会迁移到其他线程,这样能继续运行。开发者并不需要关心/担心这些细节。

下面整理了并发编程中可能会经常用到的基本知识点,主要是channel的使用以及sync包的使用。

1、channel使用

channel通常用来在多个线程之间传递消息或者进行同步,channel的基本语法这里不再赘述,直接上一个实例代码:

package mainimport ("fmt""time"
)func worker(dead <-chan bool, index int) {fmt.Println("Worker ", index, " Start!")for {select {case <-dead:fmt.Println("Worker ", index, " Done!")breakdefault ://fmt.Println("Worker ", index, "Doing...") }}
}func worker2(done chan<- int, index int) {fmt.Println("Worker ", index, " Start!")time.Sleep(time.Second * 5)done <- index
}func main() {/*//单个channel,unbuffered channel 同步用法c := make(chan bool)go func(){fmt.Println("goruntine child Test!")c <- true}()<- cfmt.Println("goruntine main Test!")  *//*//单个channel,buffered channel 异步用法c := make(chan int, 3)go func(){for i := 0; i < 4; i++ {c <- ifmt.Println("write to c: ", i)}}()for j := 0; j < 4; j++ {fmt.Println("read from c: ", <-c)}*//*//协同多个goruntine,unbuffeered channel 同步用法c := make(chan bool)fmt.Println("Start Master process!")for i := 1; i < 5; i++ {go func(index int){fmt.Println("Worker ", index, " Start!")<- cfmt.Println("Worker ", index, " Done!")}(i)}for j := 1; j < 5; j++ {c <- true}fmt.Println("Master Done!")*//*//select 用法die := make(chan bool)for i := 0; i < 5; i++ {go worker(die, i)}time.Sleep(time.Second)for i := 0; i < 5; i++ {die <- true}*/fmt.Println("Master Start!")c1 := make(chan int)c2 := make(chan int)c3 := make(chan int)c4 := make(chan int)go worker2(c1, 1)go worker2(c2, 2)go worker2(c3, 3)go worker2(c4, 4)num := 0
Done:for {select {case <- c1 :fmt.Println("Worker 1 Done!")num += 1case <- c2 :fmt.Println("Worker 2 Done!")num += 1case <- c3 :fmt.Println("Worker 3 Done!")num += 1case <- c4 :fmt.Println("Worker 4 Done!")num += 1default :if num >= 4 {break Done}}}fmt.Println("Master Done!")
}

2、sync.Cond 使用

Cond是条件等待,条件等待通过 Wait 让例程等待,通过 Signal 让一个等待的例程继续,通过 Broadcast 让所有等待的例程继续。

在 Wait 之前应当手动为 c.L 上锁,Wait 结束后手动解锁。为避免虚假唤醒,需要将 Wait 放到一个条件判断循环中。官方要求的写法如下:

c.L.Lock()
for !condition() {c.Wait()
}
// 执行条件满足之后的动作...
c.L.Unlock()

Cond 在开始使用之后,不能再被复制。

示例代码如下:

package mainimport ("fmt""time""sync"
)func waiter(cond *sync.Cond, id int){cond.L.Lock()cond.Wait()cond.L.Unlock()fmt.Println("Waiter ", id, "wake up!")
}func main() {locker := new(sync.Mutex)cond := sync.NewCond(locker)for i := 0; i < 3; i++ {go waiter(cond, i)}time.Sleep(time.Second * 3)cond.L.Lock()cond.Signal()cond.L.Unlock()for i := 3; i < 5; i++ {go waiter(cond, i)}time.Sleep(time.Second * 3)cond.L.Lock()cond.Signal()cond.L.Unlock()cond.L.Lock()cond.Broadcast()cond.L.Unlock()time.Sleep(time.Second * 5)
}

3、sync.Mutex使用

Mutex是互斥锁,用来保证在任一时刻,只能有一个例程访问某对象。Mutex 的初始值为解锁状态。Mutex 通常作为其它结构体的匿名字段使用,使该结构体具有 Lock 和 Unlock 方法。

Mutex 可以安全的在多个例程中并行使用。

示例代码如下:

package mainimport ("fmt""sync"
)func click(c chan bool, count *int) {for i := 0; i < 1000; i++ {*count += 1}c <- true
}func clickWithMutex(c chan bool, count *int, m *sync.Mutex) {for i := 0; i < 1000; i++ {m.Lock()*count += 1m.Unlock()}c <- true
}func main() {m := new(sync.Mutex)count1, count2 := 0, 0c := make(chan bool, 10)for i := 0; i < 5; i++ {go click(c, &count1)}for i := 0; i < 5; i++ {go clickWithMutex(c, &count2, m)}for i := 0; i < 10; i++ {<- c}fmt.Println("count1: ", count1)fmt.Println("count2: ", count2)
}

4、sync.Once使用

Once是单次执行。

Once 的作用是多次调用但只执行一次,Once 只有一个方法,Once.Do(),向 Do 传入一个函数,这个函数在第一次执行 Once.Do() 的时候会被调用,以后再执行 Once.Do() 将没有任何动作,即使传入了其它的函数,也不会被执行,如果要执行其它函数,需要重新创建一个 Once 对象。

Once 可以安全的在多个例程中并行使用。

示例代码:

package mainimport ("fmt""sync"
)func main() {once := new(sync.Once)ch := make(chan bool, 5)for i := 0; i < 5; i++ {go func(x int){once.Do(func(){fmt.Println("Worker Do: ", x)})fmt.Println("Master Do: ", i)ch <- true}(i)}for j := 0; j < 5; j++ {<- ch}}

5、sync.RWMutex使用

RWMutex是读写互斥锁。

RWMutex 比 Mutex 多了一个“读锁定”和“读解锁”,可以让多个例程同时读取某对象。RWMutex 的初始值为解锁状态。RWMutex 通常作为其它结构体的匿名字段使用。

Mutex 可以安全的在多个例程中并行使用。

示例代码:

package mainimport ("fmt""sync"
)func clickWithRWMutex(m *sync.RWMutex, total *int, ch chan int) {for i := 0; i < 1000; i++ {m.Lock()*total += 1m.Unlock()if i == 500 {m.RLock()fmt.Println("Middle Num: ", *total)m.RUnlock()}}ch <- 1
}func main() {m := new(sync.RWMutex)ch := make(chan int, 10)count := 0for i := 0; i < 10; i++ {go clickWithRWMutex(m, &count, ch)}for i := 0; i < 10; i++ {<- ch}fmt.Println("Count: ", count)
}

6、sync.WaitGroup使用

WaitGroup是组等待。

WaitGroup 用于等待一组例程的结束。主例程在创建每个子例程的时候先调用 Add 增加等待计数,每个子例程在结束时调用 Done 减少例程计数。之后,主例程通过 Wait 方法开始等待,直到计数器归零才继续执行。

示例代码:

package mainimport ("sync""fmt"
)func wgProcess(wg *sync.WaitGroup, index int) {fmt.Println("WgProcess ", index, "is going!")wg.Done()
}func main() {wg := new(sync.WaitGroup)for i := 0; i < 20; i++ {wg.Add(1)go wgProcess(wg, i)}wg.Wait()fmt.Println("All is Done!")
}

7、sync/atomic 使用

sync/atomic是原子操作,具体未完待续。。。。。。

参考资料:

http://blog.xiayf.cn/2015/05/20/fundamentals-of-concurrent-programming/

https://github.com/astaxie/gopkg/tree/master/sync

https://www.cnblogs.com/golove/p/5918082.html

Go 并发常用知识点实例相关推荐

  1. Java多线程并发常用类实例之:exchanger

    2019独角兽企业重金招聘Python工程师标准>>> Exchanger可以在两个线程之间交换数据,只能是2个线程,他不支持更多的线程之间互换数据. 当线程A调用Exchange对 ...

  2. javaScript常用知识点有哪些

    javaScript常用知识点有哪些 一.总结 一句话总结:int = ~~myVar, // to integer | 是二进制或, x|0 永远等于x:^为异或,同0异1,所以 x^0 还是永远等 ...

  3. ES6常用知识点概述

    前言 国庆假期已过一半,来篇干货压压惊. ES6,并不是一个新鲜的东西,ES7.ES8已经赶脚了.但是,东西不在于新,而在于总结.每个学前端的人,身边也必定有本阮老师的<ES6标准入门>或 ...

  4. oracle 常用知识点整理

    转 :  oracle 常用知识点 原文链接:http://blog.csdn.net/weijiaxiaobao/article/details/51323573 Oracle 是一个庞大的系统,里 ...

  5. php常用技术与thinkphp5,thinkPHP5框架路由常用知识点汇总

    本文实例讲述了thinkPHP5框架路由常用知识点.分享给大家供大家参考,具体如下: 一.路由的模式 普通模式(默认pathinfo,不解析路由) 'url_route_on' => false ...

  6. 【C++】-- C++11基础常用知识点(下)

    上篇: [C++]-- C++11基础常用知识点(上)_川入的博客-CSDN博客 目录 新的类功能 默认成员函数 可变参数模板 可变参数 可变参数模板 empalce lambda表达式 C++98中 ...

  7. Webpack 常用知识点总结

    Webpack 常用知识点总结 webpack 是一个模块打包器.webpack 的主要目标是将 javaScript 文件打包在一起,打包后的文件用于在浏览器中使用,但它也能够胜任转换(transf ...

  8. layu常用知识点笔记

    layu常用知识点笔记 layui帮助文档 1.上传图片 2.loading 3.时间格式化 4.layui值判断 5.组件内容回显 1)固定下拉项的选中实现 2)下拉框内容动态获取或回显实现思路 3 ...

  9. Vue 2.x 常用知识点笔记(万字笔记)

    Vue 2.x 笔记 常用知识点笔记,有错误欢迎指点哦~ 字数超过了一万字,得细品 目录 Vue 2.x 笔记 1.新建Vue实例 2.阻止生成生产提示 3.模板语法 3.1 插值语法 3.2 指令语 ...

最新文章

  1. 小测一下fastjson的速度(纯娱乐)
  2. TCP与UDP区别小结
  3. openstack——使用命令行发放云主机
  4. HTTP请求与接收get/post方式
  5. 设置XMLHttpRequest“ withCredentials”属性问题,axios请求不成功
  6. maven异常001---报错The folder is already a source folder.的解决办法
  7. MATLAB表上作业法解决运输问题
  8. 内网通修改积分文件_【页游逆向】4399小游戏积分系统分析及修改积分
  9. 双闭环直流调速系统matlab/simulink仿真
  10. win7网络上计算机进不去,win7系统电脑红色警戒2点击网络进不去的解决方法
  11. 【观察】华为IoT首席架构师王启军:云计算时代全栈工程师的养成
  12. p2p网络实现(C++)
  13. zend studio php 错误提示,win7启动Zend Studio弹出错误提示?查看解决方法
  14. c语言取位,C语言位操作
  15. MySQL无法成功启动
  16. ddd java repository_初探领域驱动设计(2)Repository在DDD中的应用
  17. EXCEL中批量计算“已知一点坐标、方位角、距离,求坐标点”
  18. 无人机远距离WiFi图传应用,CV5200无线WiFi模组,远程实时通信传输方案
  19. 指南 | 广播电视节目制作经营许可证
  20. php网页怎么加入百度地图,PHP如何通过URL访问,获得新的URL 【调用百度地图】...

热门文章

  1. [转载]《狼》-bressanon布列瑟农 一次触动每个人心灵的感受
  2. 静态html左侧导航菜单代码,Html+Css+Jquery实现左侧滑动拉伸导航菜单栏的示例代码...
  3. 微软跨平台ORM框架之EFCore
  4. Log抓取和分析-BugReport
  5. 提升固定资产管理效率,一招轻松制胜
  6. 深度学习笔记(MNIST手写识别)
  7. HMI-37-【节能模式】中心仪表实现
  8. DWMAC网卡的相关兼容
  9. 抖音火山小视频上线无障碍版本 视障用户也能看短视频了
  10. win11怎么更改电源模式?win11更改电源模式的方法