• golang并发编程基础


    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结束
    }
    ​
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    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
        }
    }
    ​
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    加锁之后代码成功运行

    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()
        }
    }
    ​
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    锁不能复制。

    更加优雅的方式,使用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)
        }
    }
    ​
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    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)
    ​
    }
    ​
    ​
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    无缓冲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)
    ​
    }
    ​
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    模拟单项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)
    }
    ​
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    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)
    }
    ​
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
  • 相关阅读:
    4G网关BL100链接私有云平台教程
    【数据结构与算法】二分查找算法
    LeetCode之二叉树
    分享一下便利店怎么做微信小程序
    AtCoder Beginner Contest 319(D-G)
    直方图(Histogram)的统计说明
    Unity中的MonoBehaviour脚本-基础知识和继承关系
    【二分图染色】ARC 165 C
    如何获取淘宝sku详细信息 API接口
    Raft共识算法浅谈
  • 原文地址:https://blog.csdn.net/giaogege666/article/details/132853557