广告位联系
返回顶部
分享到

Go语言等待组sync.WaitGrou的使用

Golang 来源:互联网 作者:佚名 发布时间:2024-08-17 22:14:47 人浏览
摘要

Go语言中除了可以使用通道(channel)和互斥锁进行两个并发程序间的同步外,还可以使用等待组进行多个任务的同步,等待组可以保证在并发环境中完成指定数量的任务 在 sync.WaitGroup(等待组

Go语言中除了可以使用通道(channel)和互斥锁进行两个并发程序间的同步外,还可以使用等待组进行多个任务的同步,等待组可以保证在并发环境中完成指定数量的任务

在 sync.WaitGroup(等待组)类型中,每个 sync.WaitGroup 值在内部维护着一个计数,此计数的初始默认值为零。

等待组有下面几个方法可用,如下表所示。

方法名 功能
(wg * WaitGroup) Add(delta int) 等待组的计数器 +1
(wg * WaitGroup) Done() 等待组的计数器 -1
(wg * WaitGroup) Wait() 当等待组计数器不等于 0 时阻塞直到变 0。

对于一个可寻址的 sync.WaitGroup 值 wg:

  • 我们可以使用方法调用 wg.Add(delta) 来改变值 wg 维护的计数。
  • 方法调用 wg.Done() 和 wg.Add(-1) 是完全等价的。
  • 如果一个 wg.Add(delta) 或者 wg.Done() 调用将 wg 维护的计数更改成一个负数,一个恐慌将产生。
  • 当一个协程调用了 wg.Wait() 时,
    • 如果此时 wg 维护的计数为零,则此 wg.Wait() 此操作为一个空操作(noop);
    • 否则(计数为一个正整数),此协程将进入阻塞状态。当以后其它某个协程将此计数更改至 0 时(一般通过调用 wg.Done()),此协程将重新进入运行状态(即 wg.Wait() 将返回)。

等待组内部拥有一个计数器,计数器的值可以通过方法调用实现计数器的增加和减少。当我们添加了 N 个并发任务进行工作时,就将等待组的计数器值增加 N。每个任务完成时,这个值减 1。同时,在另外一个 goroutine 中等待这个等待组的计数器值为 0 时,表示所有任务已经完成。

下面的代码演示了这一过程:

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

package main

import (

    "fmt"

    "net/http"

    "sync"

)

func main() {

    // 声明一个等待组

    var wg sync.WaitGroup

    // 准备一系列的网站地址

    var urls = []string{

        "http://www.github.com/",

        "https://www.qiniu.com/",

        "https://www.golangtc.com/",

    }

    // 遍历这些地址

    for _, url := range urls {

        // 每一个任务开始时, 将等待组增加1

        wg.Add(1)

        // 开启一个并发

        go func(url string) {

            // 使用defer, 表示函数完成时将等待组值减1

            defer wg.Done()

            // 使用http访问提供的地址

            _, err := http.Get(url)

            // 访问完成后, 打印地址和可能发生的错误

            fmt.Println(url, err)

            // 通过参数传递url地址

        }(url)

    }

    // 等待所有的任务完成

    wg.Wait()

    fmt.Println("over")

}

代码说明如下:

  • 第 12 行,声明一个等待组,对一组等待任务只需要一个等待组,而不需要每一个任务都使用一个等待组。
  • 第 15 行,准备一系列可访问的网站地址的字符串切片。
  • 第 22 行,遍历这些字符串切片。
  • 第 25 行,将等待组的计数器加1,也就是每一个任务加 1。
  • 第 28 行,将一个匿名函数开启并发。
  • 第 31 行,在匿名函数结束时会执行这一句以表示任务完成。wg.Done() 方法等效于执行 wg.Add(-1)。
  • 第 34 行,使用 http 包提供的 Get() 函数对 url 进行访问,Get() 函数会一直阻塞直到网站响应或者超时。
  • 第 37 行,在网站响应和超时后,打印这个网站的地址和可能发生的错误。
  • 第 40 行,这里将 url 通过 goroutine 的参数进行传递,是为了避免 url 变量通过闭包放入匿名函数后又被修改的问题。
  • 第 44 行,等待所有的网站都响应或者超时后,任务完成,Wait 就会停止阻塞。

例子

同时开三个协程去请求网页, 等三个请求都完成后才继续 Wait 之后的工作

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

var wg sync.WaitGroup

var urls = []string{

   "http://www.golang.org/",

   "http://www.google.com/",

   "http://www.somestupidname.com/",

}

for _, url := range urls {

   // Increment the WaitGroup counter.

   wg.Add(1)

   // Launch a goroutine to fetch the URL.

   go func(url string) {

      // Decrement the counter when the goroutine completes.

      defer wg.Done()

      // Fetch the URL.

      http.Get(url)

   }(url)

}

// Wait for all HTTP fetches to complete.

wg.Wait()

或者下面的代码
用于测试 给chan发送 1千万次,并接受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

37

import (

   "fmt"

    "sync"

    "time"

)

 

const (

   num = 10000000

)

 

func main() {

   TestFunc("testchan", TestChan)

}

 

func TestFunc(name string, f func()) {

   st := time.Now().UnixNano()

   f()

   fmt.Printf("task %s cost %d \r\n", name, (time.Now().UnixNano()-st)/int64(time.Millisecond))

}

 

func TestChan() {

   var wg sync.WaitGroup

   c := make(chan string)

   wg.Add(1)

 

   go func() {

      for _ = range c {

      }

      wg.Done()

   }()

 

   for i := 0; i < num; i++ {

      c <- "123"

   }

   close(c)

   wg.Wait()

}


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 :
相关文章
  • golang interface指针实现
    在 Go 语言中,接口(interface)是一种类型,它定义了一组方法的集合。任何实现了接口中所有方法的类型都会自动满足该接口。当涉及到指针
  • Go语言等待组sync.WaitGrou的使用
    Go语言中除了可以使用通道(channel)和互斥锁进行两个并发程序间的同步外,还可以使用等待组进行多个任务的同步,等待组可以保证在并
  • Go语言中的接口类型介绍
    接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节。 1.接口类型 1.1 接口类型的说明 Go语言中
  • Go语言占位符的使用介绍
    Golang的字符串占位符在fmt包的各种打印函数中使用,如fmt.Printf、fmt.Sprintf。 变量值与类型的打印 %v: 打印变量的值 %v会根据变量的类型选择
  • 基于Golang实现Kubernetes边车模式

    基于Golang实现Kubernetes边车模式
    在这篇文章中,我们会介绍 Sidecar 模式,并创建两个基于 Go 的容器化微服务,然后部署到 Kubernetes 上。 什么是 Sidecar 模式? Sidecar 模式是一
  • 使用Go语言实现基于泛型的Jaccard相似度算法

    使用Go语言实现基于泛型的Jaccard相似度算法
    基本原理 跳表: jaccard相似度: jaccard相似度的代码实现: 时间复杂度分析: 快速jaccard算法: 代码实现,这个要求两个集合都是有序的:
  • go-zero自定义中间件的几种方式
    1.通过 api 文件生成并填入具体逻辑 定义 api 首先你需要在 api 文件中定义你需要的路由位置和中间件的名字: 1 2 3 4 5 6 7 8 9 // test for ping @
  • Go中gin框架的*gin.Context参数常见实用方法
    梗概: *gin.Context是处理HTTP请求的核心。ctx代表context(上下文),它包含了处理请求所需的所有信息和方法,例如请求数据、响应构建器、
  • Python实现图像添加水印的方法
    在日常图像处理中,为图片添加水印是一项常见任务。有多种方法和工具可供选择,而今天我们将专注于使用Python语言结合PIL库批量添加水
  • go的defer和return的执行顺序介绍
    go的defer和return的执行顺序 go的defer和return是golang中的两个关键字,return用于返回函数的返回值,也可以参与一定的流程控制,比如下面代码
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计