在golang中使用Sync.WaitGroup解决等待的问题
作者:七八月份的太阳 发布时间:2024-04-26 17:30:17
面对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.结果
还是对指针传递以及值传递理解不够透彻才会出现如此问题,因此还需要多针对指针进行学习与理解。以防在以后的工作中出现类似问题。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持asp之家。如有错误或未考虑完全的地方,望不吝赐教。
来源:https://wangjiayu.blog.csdn.net/article/details/90217170
猜你喜欢
- 【需求背景】有时候我们要对比两份配置文件是不是一样,或者比较两个文本是否异样,可以使用linux命令行工具diff a_file b_fil
- 这就意味着数据库和表名在 Windows 中是大小写不敏感的,而在大多数类型的 Unix 系统中是大小写敏感的。一个特例是 Mac OS X
- golang的defer优雅又简洁, 是golang的亮点之一。defer在声明时不会立即执行,而是在函数return后,再按照先进后出的原
- 前言:索引下推(ICP)是针对MySQL使用索引从表中检索数据行的情况的优在没有索引下推的情况下,MySQL通过存储引擎遍历索引来定位表中的
- 步骤一:设置sql server数据库1.登录sql server数据库以新建一个新用户名test作为远程连接登录名。在本地登录sql se
- 如何使用表单发送电子邮件?邮件也可以用表单格式发送吗?我见一个朋友这样做的。当然可以,用OCXMail就行:formToEmail.htm&
- 本教程为大家分享了Python搭建HTTP服务器和FTP服务器的具体步骤,供大家参考,具体内容如下默认安装版本为pytho2.7http服务
- 今天的这篇文章是讲XHTML中的细节部分的,这篇续述的主题就是ID与CLASS怎么用,在标题中有提及使用原则与技巧,这里的使用原则与技巧是我
- 这篇文章主要介绍了Python socket模块方法实现详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需
- 一行命令搭建一个基于python的http文件传输服务由于今天朋友想要一个文件,而我恰好有,因为这个文件比较大,网速不是很给力,所以想到了p
- 1.writelines()直接写入l=["A","B","C","D
- 科讯5.0 标签和之前版本变化不大,如果用老版本的科讯,可以参考这个标签使用。相关文章:新云4.0 模板通用标签说明 标签清单:======
- 前言近期在刷新生产环境数据库的时候,需要更新表中的字段,如果对每条数据结果都执行一次update语句,占用的数据库资源就会很多,而且速度慢。
- 表中主键必须为标识列,[ID] int IDENTITY (1,1)1.分页方案一:(利用Not In和SELECT TOP分页)语句形式:
- 在设计主键的时候往往需要考虑以下几点: 1.无意义性:此处无意义是从用户的角度来定义的。这种无意义在一定程度上也会减少数据库的信息冗余。常常
- 一、流程分析:1.用户在项目前端,输入手机号,然后点击【获取验证码】,将手机号发到post到后台。2.后台验证手机号是否合法,是否已被占用,
- 简单的for循环打印三角形1,for循环方法实现星星三角代码:for i in range(0,5):for j in range(i+1)
- 最近更新了VS Code之后,发现Remote-ssh拓展里的端口转发功能没了,很伤心,在探索的同时,顺手配置了一下VS Code ssh免
- 代码自动填充了空格 问题在使用pycharm的代码编辑器时,常常懒得写空格,如下图,但这是不符合代码规范的,而且也会影响可读性。解决方法py
- 工具与图书馆Python-3.xCV2-4.5.2矮胖-1.20.3人脸识别-1.3.0若要安装上述软件包,请使用以下命令。pip inst