• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    在golang中使用Sync.WaitGroup解决等待的问题

    面对goroutine我们都需要等待它完成交给它的事情,等待它计算完成或是执行完毕,所以不得不在程序需要等待的地方使用time.Sleep()来睡眠一段时间,等待其他goroytine执行完毕,下面的代码打印1到100的for循环可以在很快的时间内运行完毕,但是我们必须添加time.Sleep()来等待其打印完毕,如果我们不等待仿佛什么也没有发生一样。。。。。这肯定不是我们想要的!

    func main(){
        for i := 0; i  100 ; i++{
            go fmt.Println(i)
        }
        time.Sleep(time.Second)
    }

    这就是我最开始学习goroutine想到的笨方法。。。。死等! 等不到 time.Second*2 在等不到*3

    但是对于我们实际的使用场景来说,1秒也许等待的时间有点长,而有时候是远远不够的,最主要的我们无法预知gorotine的运行时间长短,所以也无法指定Sleep睡眠的时间。

    正好前几天学习了channel,那么现在我们就可以

    使用channel来达到我们的目的了

    func main() {
        c := make(chan bool, 100)
        for i := 0; i  100; i++ {
            go func(i int) {
                fmt.Println(i)
                c - true
            }(i)
        }
     
        for i := 0; i  100; i++ {
            -c
        }
    }

    完美输出!这里我们可以从控制台看到我们的胜利成果,但是我们有更好的处理方式:sync.WaitGroup

    WaitGroup对象内部有一个计数器,最初从0开始,它有三个方法:Add() Done() Wait() 用来控制计数器的数量从而达到等待的目的。

    func main() {
       wg := sync.WaitGroup{}
       wg.Add(100)
       for i := 0; i  100; i++ {
          go func(i int) {
             defer wg.Done()
             fmt.Print(i)
          }(i)
       }
       wg.Wait()
    }

    首先使用 add(100) 把计数器的数量设置为100,每次循环运行完毕都把计数器减1,主函数使用Wait()

    阻塞,直到计数器归0结束主线程结束阻塞。

    这里会阻塞主线程!!!需要注意。

    另外:以上三种方式打印出的数字皆无序

    type WaitGroup struct {
     noCopy noCopy
     state1 [3]uint32
    }

    它只是一个struct所以当参数传递的时候需要传递指针。

    补充:sync.WaitGroup的错误用法与纠正

    本文主要是记录在学习golang语言过程中错误使用sync.WaitGroup以及指针引起的报错,并且纠正过程。

    1.错误使用场景

    main包的实现内容:

    package main
    import (
        "fmt"
        "pressure/game"
        "sync"
    )
    var waitGroup sync.WaitGroup //定义一个同步等待的组
    func main(){
        waitGroup.Add(1) //添加一个计数
        go game.ConnSocket(serverAddr, waitGroup) //调用其他包的方法执行任务
        waitGroup.Wait() //阻塞直到所有任务完成
        fmt.Println("main DONE!!!")
    }
    

    如下是game包的内容:

    package game
    import (
        "fmt"
        "net"
        "strconv"
        "strings"
        "sync"
    )
    var gameWait sync.WaitGroup
    func ConnSocket(serverAddr string, wait sync.WaitGroup) {
        var err error
        Conn, err = net.Dial("tcp", serverAddr)
        if err != nil {
            fmt.Println("Error connected:", err.Error())
            wait.Done()
            return
        }
        gameWait = wait
        fmt.Println("connected OK:", Conn.RemoteAddr())
        go readMessage()
    }
    func readMessage() {
        fmt.Println("readMessage:")
        for {
            if OK:=dosomething(),OK{
                gameWait.Done()
            }
        }
    }
    

    然后执行如上的代码,会出现报错:

    panic: sync: negative WaitGroup counter

    goroutine 12 [running]:

    sync.(*WaitGroup).Add(0x7b8b90, 0xffffffffffffffff)

    D:/Program Files/Go/src/sync/waitgroup.go:75 +0x1d0

    sync.(*WaitGroup).Done(0x7b8b90)

    原因:

    func ConnSocket(serverAddr string, wait sync.WaitGroup)只是把sync.WaitGroup的值传递过来,而非内存地址,所以在赋值给gameWait的时候也只是赋值了值,gameWait并非指向的main里的waitGroup 的内存地址,导致 gameWait.Done()的时候并没有获取到main里的计数1,所以才会报负数。究其原因还是没有对指针的理解。所以针对上面的问题做了修改。

    2.正确的使用

    main包的实现内容:

    package main
    import (
        "fmt"
        "pressure/game"
        "sync"
    )
    var waitGroup sync.WaitGroup //定义一个同步等待的组
    func main(){
        waitGroup.Add(1) //添加一个计数
        //这里传递waitGroup的内存地址
        go game.ConnSocket(serverAddr, waitGroup) //调用其他包的方法执行任务
        waitGroup.Wait() //阻塞直到所有任务完成
        fmt.Println("main DONE!!!")
    }
    

    如下是game包的内容:

    package game
    import (
        "fmt"
        "net"
        "strconv"
        "strings"
        "sync"
    )
    var gameWait *sync.WaitGroup//此处也申明为指针变量
    //参数传递为指针传递*sync.WaitGroup,即wait 指向的是main里的waitGroup的内存地址
    func ConnSocket(serverAddr string, wait *sync.WaitGroup) {
        var err error
        Conn, err = net.Dial("tcp", serverAddr)
        if err != nil {
            fmt.Println("Error connected:", err.Error())
            wait.Done()
            return
        }
        gameWait = wait//指针指向另一个指针,这样就做到了gameWait指向的是wait的地址,所以gameWait修改wait的内存地址锁存贮的值也会跟随改变
        fmt.Println("connected OK:", Conn.RemoteAddr())
        go readMessage()
    }
    func readMessage() {
        fmt.Println("readMessage:")
        for {
            if OK:=dosomething(),OK{
                gameWait.Done()
            }
        }
    }
    

    3.结果

    还是对指针传递以及值传递理解不够透彻才会出现如此问题,因此还需要多针对指针进行学习与理解。以防在以后的工作中出现类似问题。

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。如有错误或未考虑完全的地方,望不吝赐教。

    您可能感兴趣的文章:
    • 解决Golang 中使用WaitGroup的那点坑
    • Golang中的sync包的WaitGroup操作
    • Golang中的sync.WaitGroup用法实例
    • Golang标准库syscall详解(什么是系统调用)
    • Golang的os标准库中常用函数的整理介绍
    • Golang 标准库 tips之waitgroup详解
    上一篇:Golang模拟令牌桶进行对访问的限流方式
    下一篇:Golang 之协程的用法讲解
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    在golang中使用Sync.WaitGroup解决等待的问题 在,golang,中,使用,Sync.WaitGroup,