go语言之路

go高并发之锁的概念(资源竞争)以及channel的使用

go高并发之锁的概念(资源竞争)

本文纯享版
案例:

package main

import (
    "fmt"
    "time"
)

//goroutine map 出现资源竞争

//需求分析:

/*
    计算1-200的各个数的阶层,并且把各个数的阶层放入map中,最后遍历输出
    要求使用goroutine完成
*/

var maps = make(map[int]int, 200)

func fac(n int) {
    res := 1
    for i := 1; i <= n; i++ {
        res = res * i
    }
    maps[n] = res

}

func main()  {
    //计算1-200的阶乘
    for n := 1; n <= 200; n++ {
        go fac(n)
    }

    time.Sleep( 10 * time.Second )

    for index, value := range maps {
        fmt.Printf("maps[%v] = %v", index, value)
    }
}

致命错误:

fatal error: concurrent map writes

原因分析

200个协程同时向一块map空间写入数据

导致资源竞争的问题

于是了锁的概念

思路分析:

var (

    maps = make(map[int]int, 200)
    //加锁
    // lock是一个全局的互斥锁
    //mutex是互斥
    lock sync.Mutex

)
func fac(n int) {
    res := 1
    for i := 1; i <= n; i++ {
        res = res * i
    }
    //写之前加锁
    lock.Lock()
    maps[n] = res
    //写完解锁
    lock.Unlock()
}

加锁的机制保证了线程的安全

但是我们发觉的问题有:

不知道协程什么时候结束

因为在go中,主线程是不等待协程的

所以两者之间必须要有个通信机制

使用time.Sleep的做法很蠢,也不方便

于是

channel出场

前面使用全局变量加锁同步来解决goroutine的通讯,不太完美

主线程在等待所有的协程全部完成的时间很难确定

通过全局变量加锁同步来实现通讯,不适用于多个协程对全局变量的读写操作

于是有了新的通讯机制channel

channel是线程安全的

当多个协程操作同一个管道时,不会发生资源竞争的问题

数据是先进先出的

队列

入门案例

package main

import (
    "fmt"
)

//管道的使用

func main()  {

    intChan := make(chan int, 10)
    for i := 0; i < 10; i++ {
        //把i放进管道中
        intChan <- i
    }

    var arrayChan [10]int

    for i := 0; i < 10; i++ {
        //把管道中的数据输出
        arrayChan[i] = <- intChan
    }

    fmt.Println(arrayChan)
}

输出

[0 1 2 3 4 5 6 7 8 9]

符合先进先出的规则

并且管道的大小是取一个少一个

容量是不发生变化的

通过去查看通道的长度便知道了

案例:使用通道解决上述的求阶乘的问题

package main

import "fmt"

var intChan = make(chan int, 1)

func fac(n int) {
   res := 1
   for i := 1; i <= n; i++ {
      res = res * i
   }
   intChan <- res
}

func main()  {

   //计算1-20的阶乘
   for n := 1; n <= 20; n++ {
      go fac(n)
      resChan := <- intChan
      fmt.Printf("%d! = %d\n", n, resChan)
   }
}

只需给intChan一个长度即可

程序中每放进一个intChan

就读取一个intChan

节省了空间

也节省了时间

经测试

算200000个数的阶层

使用goroutine + channel 使用的时间是 646ms

而使用普通的的代码

计算阶层的时间是40s

ubuntu系统下如何使用systemctl管理系统服务

上一篇

docker的介绍以及基本使用(1)

下一篇

你也可能喜欢

发表评论

您的电子邮件地址不会被公开。 必填项已用 * 标注

提示:点击验证后方可评论!

插入图片

个人微信公众号

we-tuiguang

qq交流群

群号:1046260719

微信扫一扫

微信扫一扫