golang并发编程基础

2023-09-13 14:42:45

go并发编程

1waitgroup

WaitGroup就是等待所有的goroutine全部执行完毕,add方式和Down方法要配套使用

package main
​
import (
    "fmt"
    "sync"
)
​
func main()  {
    var wq sync.WaitGroup
​
    wq.Add(100) //监控多少个goroutine执行结束
​
    for i:= 0;i<100;i++ {
        //开启一个协程
        go func(i int) {
            defer wq.Done() //和add是一起使用的
            fmt.Println(i)
        }(i)
    }
​
    wq.Wait() //等待所有的goroutine结束
}
​

2通过锁来完成全局变量的原子性操作

开启两个gorountine对total进行相同此时的加减,但是这一段程序的运行结果每一次都不一样

资源竞争,加锁

package main
​
import (
    "fmt"
    "sync"
)
​
var total int
var wg sync.WaitGroup
​
func main() {
    wg.Add(2)
    go add()
    go sub()
    wg.Wait()
    fmt.Println(total)
}
func add() {
    defer wg.Done()
    for i := 0; i < 100000; i++ {
        total += 1
    }
}
​
func sub() {
    defer wg.Done()
    for i := 0; i < 100000; i++ {
        total -= 1
    }
}
​

加锁之后代码成功运行

package main
​
import (
    "fmt"
    "sync"
)
​
var total int
var wg sync.WaitGroup
var lock sync.Mutex
​
func main() {
    wg.Add(2)
    go add()
    go sub()
    wg.Wait()
    fmt.Println(total)
}
func add() {
    defer wg.Done()
    for i := 0; i < 100000; i++ {
        lock.Lock()
        total += 1
        lock.Unlock()
    }
}
​
func sub() {
    defer wg.Done()
    for i := 0; i < 100000; i++ {
        lock.Lock()
        total -= 1
        lock.Unlock()
    }
}
​

锁不能复制。

更加优雅的方式,使用golang的原子包

package main
​
import (
    "fmt"
    "sync"
    "sync/atomic"
)
​
var total int64
var wg sync.WaitGroup
​
//var lock sync.Mutex
​
func main() {
    wg.Add(2)
    go add()
    go sub()
    wg.Wait()
    fmt.Println(total)
}
func add() {
    defer wg.Done()
    for i := 0; i < 100000; i++ {
​
        atomic.AddInt64(&total, 1)  //原子性的操作
​
    }
}
​
func sub() {
    defer wg.Done()
    for i := 0; i < 100000; i++ {
        atomic.AddInt64(&total,-1)
    }
}
​

lock相对atomic性能较差,lock基于操作系统调度

3读写锁

锁实际上是将并行的代码串行化了,使用lock肯定影响性能,即使是设计所,也应该尽量保证并行

4goroutine进行通讯

不要通过共享内存来通讯,而要通过通讯来实现内存共享

channel的基础用法

package main
​
import "fmt"
​
func main()  {
    // 名字  类型  存储类型
    var msg chan string //默认值未nil
​
    msg = make(chan string ,1)  //channel的初始化的值如果是0的话,放值进去会阻塞,如果设为0就为无缓冲channel
​
    msg <- "大大怪" //将右边的值放在channel中
    name := <- msg //将channel中的值取出来给name
    fmt.Println(name)
​
}
​
​

无缓冲channel用法

package main
​
import (
    "fmt"
    "time"
)
​
func main() {
    // 名字  类型  存储类型
    var msg chan string //默认值未nil
​
    msg = make(chan string, 0) //channel的初始化的值如果是0的话,放值进去会阻塞,如果设为0就为无缓冲channel
    go func(msg chan string) {
        name := <-msg //将channel中的值取出来给name
        fmt.Println(name)
    }(msg)
    msg <- "大大怪"  //将右边的值放在channel中
​
time.Sleep(time.Second*10)
​
}
​

waitgroup少了一个done容易出现deadlock

无缓冲的channel也容易出现deadlock

适用场景

无缓冲channel适用于通知B要第一时间知道A是否已经完成

有缓冲channel适用于生产者和消费者之间的通讯

go中channel的应用场景

  • 消息传递,消息过滤
  • 信号广播
  • 事件订阅和广播
  • 任务分发
  • 结果汇总
  • 并发控制
  • 同步和异步

5.单项channel的使用

默认情况下,channel是双向的,但是我们经常一个channel作为参数进行传递,希望对象也是单向使用

package main
​
import "fmt"
​
func main() {
    //var ch1 chan int //双向的channel
    //var ch2 chan<- float64 //单项channel,只能写入float64的数据
    //var ch3 <-chan int //只能读取int类型的数据
​
    /*
    定义一个channel然后把它编程单向的,但是不能把单项的channel转成双向的channel
     */
    c := make(chan int, 3)
    var send chan<- int = c
    var read <-chan int = c
​
    send <- 1
    num := <- read
​
    fmt.Println(num)
}

模拟单项channel存取数据

package main
​
import (
    "fmt"
    "time"
)
​
func producer(out chan<- int)  {
    for i:=0;i<10 ;i++  {
        out <- i*i
    }
    close(out)
}
​
func consumer(in <-chan int)  {
    for num := range in {
        fmt.Println(num)
    }
}
func main() {
    /*
    内部会完成自动的类型转换
     */
    c := make(chan int)
    go producer(c)
    go consumer(c)
    time.Sleep(10*time.Second)
}
​

6交替打印

这是一道经典题目,在Java中也有提到,交替打印这个序列

12ab34cd56ef78gh910ij1112kl1314mn1516op1718qr1920st2122uv2324wx2526yz2728

利用channel阻塞的特性来实现

package main
​
import (
    "fmt"
    "time"
)
​
var number, letter = make(chan bool), make(chan bool)
​
func printNum() {
    i := 1
    for {
        //等待另外一个goroutine进行通知
        <-number //从number进行取值的操作,如果没有值就阻塞
        fmt.Printf("%d%d", i, i+1)
        i += 2
        letter <- true
    }
}
func printLetter() {
    str := "abcdefghijklmnopqrstuvwxyz"
    i := 0
    for {
        <-letter
        if i>= len(str){
            return
        }
            fmt.Print(str[i : i+2])
        i += 2
        number <- true // 存入true到channel中
    }
}
​
func main() {
    go printNum()
    go printLetter()
    number <- true
    time.Sleep(10*time.Second)
}
​
更多推荐

数据结构——C++实现二叉搜索树,前中后序、层序迭代遍历配合仿函数

通过介绍二叉搜索树,到实现最基础的二叉树模型,四种迭代遍历方式。结点模型template<classType>classbinary_tree{/*二叉树是由多个结点组成的,所以定义一个内部的结点类用于构建树*/classBTNode{/*不允许无参构造,因为编译器会对m_val采用默认构造,如果是int类型会导致随机

地牢大师问题(bfs提高训练 + 免去边界处理的特殊方法)

地牢大师问题文章目录地牢大师问题前言题目描述题目分析输入处理移动方式【和二维的对比】边界判断问题的解决代码总结前言在之前的博客里面,我们介绍了bfs基础算法的模版和应用,这里我们再挑战一下自己,尝试一个更高水平的题目,加深一下对bfs算法的理解。如果对bfs更多知识感兴趣的话,可以点个关注,后续会继续更新有关知识点的。

2023/9/15 -- C++/QT

作业:1>将工程文件进行注释2>03login_box.pro:QT+=coregui#core核心库gui图形开发库greaterThan(QT_MAJOR_VERSION,4):QT+=widgets#4.0版本以上自动包含widgets库CONFIG+=c++11#支持C++11版本#Thefollowingde

如何在百度百科建立个人词条,百度词条建立的过程是怎样的

如何在百度百科建立个人词条,很多朋友对百科全书这些问题都非常感兴趣,特别是很多朋友比较关心如何在百度百科上创建自己,下面洛希爱做百科网分享百度百科词条建立的过程,让大家明白创建百科词条是怎么样的。一、准备工作百度百科词条平台不能自己创建是个不正确的说法,自己完全可以再百度百科平台编辑。主要先做好以下准备工作:收集资料:

书剑宠物疫苗接种管理软件操作教程

【软件简介】书剑宠物疫苗接种管理软件是一款宠物疫苗接种管理的工具,适合宠物诊所使用。具有动物主人建档、宠物疫苗接种登记管理、每日提醒、打印疫苗接种通知卡、自定义短信提醒模板等完善的功能。另外本软件的特色是同时具有手机网页版功能,手机扫一扫即能接到电脑数据库,然后在手机上批量打电话,发短信,提醒疫苗接种即将到期的宠物主人

支持国密浏览器的堡垒机叫什么?联系电话多少?

支持国密浏览器的堡垒机叫什么?联系电话多少?目前市面上支持国密浏览器的堡垒机不多,这里给大家推荐行云管家堡垒机,其支持国密算法、国密浏览器,满足数据安全要求。行云管家堡垒机优势1、技术领先:技术团队实力雄厚,自主研发,原厂商;2、服务优质:提供24小时不间断的技术支持和服务;3、经验丰富:拥有10万+中小企业客户;4、

Command not found 解决方法

前言:要更新code上服务器用GUI失败,$patch_delivery_gui,报错:patch_delivery_gui:commandnotfound,上次编TA也是这个问题写了个脚本:这个脚本会先检查ifconfig、firewall-cmd和vim命令是否可用,如果不可用,则尝试安装相应的软件包。然后,它会显

从Vue 2到Vue 3:深入了解路由配置的变化与升级建议

🎬岸边的风:个人主页🔥个人专栏:《VUE》《javaScript》⛺️生活的理想,就是为了理想的生活!目录📘前言vue2路由配置📟一、控制台安装vue路由📟二、项目src文件夹下创建router文件夹,并在router文件夹下创建index.js文件📟三、在index.js文件夹下进行vue路由配置📟四、

【云原生 | 56】Docker三剑客之Docker Swarm高效使用

🍁博主简介:🏅云计算领域优质创作者🏅2022年CSDN新星计划python赛道第一名🏅2022年CSDN原力计划优质作者🏅阿里云ACE认证高级工程师🏅阿里云开发者社区专家博主💊交流社区:CSDN云计算交流社区欢迎您的加入!目录1.创建集群id2.配置集群节点3.配置管理节点4.查看集群节点列表5.使用集群

软考 - 计算机组成与体系笔记

数据的表示进制转化二进制转十进制(十进制以D表示)从右往左,用二进制位上的数字乘以2的n次幂的和(n从0开始+1累加)十进制转二进制(二进制以B表示)十进制数不断除以2直至到0,得到的余数按从下而上的顺序排列得到的数值二进制与八进制(八进制以O或Q表示)二进制从右往左,每三位对应的都是八进制的一位数二进制与十六进制(十

LeetCode 753. 破解保险箱【欧拉回路,DFS】困难

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及

热文推荐