sync包sync.WaitGroup 和 sync.Once

sync

sync包提供了许多功能,比如sync.WaitGroup保证所有gorountinue都能执行完
sync.Once 保证在多线程情况只执行一次等等。下面分别说下:

1. sync.WaitGroup 保证所有gorountine执行完

它主要是通过:

  1. Add() gorountine计数+1
  2. Done() 表明 当前gorountine执行完
  3. Wait() 等待所有gorountine执行完
    通过以上三个方法实现,看代码
package main

import (
    "fmt"
    "sync"
    "time"
)

var w sync.WaitGroup

func main() {
    w.Add(1) // 添加一个gorountine,开启gorountine前调用
    go func() {
        defer w.Done() // 保证执行完会标记此gorountine执行过
        for i := 0; i < 10; i++ {
            fmt.Println("i:", i)
            time.Sleep(time.Second)
        }
    }()

    w.Wait() // 等待所有gorountine执行完
    fmt.Println("finished!")
}

2. sync.Once保证在多线程情况下也只执行一次

在真实项目中,我们会存在许多情况,要求多线程只执行一次(不用多次重复执行);
比如:

  1. 更新缓存(一个线程去更新时,其它线程不要再去更新)
  2. 初始化配置数据

虽然通过加锁也达到只执行一次的目的;但是性能不好;go专门为上述场景,设计了sync.Once
,它内部提供了一个Do()方法,参数是一个函数,来实现。

看代码:

package main

import (
    "fmt"
    "sync"
)

var (
    w    sync.WaitGroup
    once sync.Once
)

// 初始化配置文件
func initConfig() {
    fmt.Println("执行初始化配置文件!")
}

func main() {
    // 添加10个 gorountine
    for i := 0; i < 100; i++ {
        w.Add(1)
        go func() {
            defer w.Done()
            // 虽然有多个gorountine执行,但是只有一个gorountine会真正执行initConfig函数
            once.Do(initConfig) //
        }()
    }

    w.Wait()
}

执行后你会看到只打印了一次 "执行初始化配置文件!";而不是10次;说明once有效;

3. sync.Map 线程安全的map

通用的map它是非线程安全的,多个gorountine下会有问题;

比如下面代码:

package main

import (
    "fmt"
    "strconv"
    "sync"
)

var w sync.WaitGroup
var m = make(map[string]int) // 通用map 非线程安全

func main() {
    // 添加10个 gorountine
    for i := 0; i < 10; i++ {
        w.Add(1)
        go func() {
            defer w.Done()
            for j := 0; j < 10; i++ {
                key := strconv.Itoa(i) // 转整数为字符串
                m[key] = j             // 设置值
                fmt.Printf("key: %s; val: %d\n", key, j)
            }
        }()
    }

    w.Wait()
}

会报错fatal error: concurrent map writes 不允许并发写map

sync.Map 提供了Store/Load 用于存/取等方法,正确代码如下:

package main

import (
    "fmt"
    "strconv"
    "sync"
)

var (
    w sync.WaitGroup
    m sync.Map // 线程安全map
)

func main() {
    // 添加10个 gorountine
    for i := 0; i < 10; i++ {
        w.Add(1)
        go func() {
            defer w.Done()
            for j := 0; j < 10; j++ {
                key := strconv.Itoa(j) // 转整数为字符串
                m.Store(key, j)        // 设置值
                val, _ := m.Load(key)
                fmt.Printf("key: %s; val: %d\n", key, val)
            }
        }()
    }

    w.Wait()
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 原文链接:https://blog.csdn.net/chenguolinblog/article/details...
    逆水寻洲阅读 1,366评论 0 1
  • [TOC] GO通道和 sync 包的分享 我们一起回顾一下上次分享的内容: GO协程同步若不做限制的话,会产生数...
    阿兵云原生阅读 226评论 0 1
  • Go语言中的并发编程 并发是编程里面一个非常重要的概念,Go语言在语言层面天生支持并发,这也是Go语言流行的一个很...
    吴佳浩阅读 371评论 0 1
  • 《Go语言四十二章经》第二十三章 同步与锁 作者:李骁 23.1 同步锁 Go语言包中的sync包提供了两种锁类型...
    ffhelicopter阅读 1,482评论 3 3
  • 并发是编程里面一个非常重要的概念,Go语言在语言层面天生支持并发,这也是Go语言流行的一个很重要的原因。 Go语言...
    雪上霜阅读 251评论 0 0