Golang中go routines的使用示例与问题讨论

Golang中go routines的使用示例与问题讨论 https://play.golang.org/p/aCuUSqnRKIy

这段代码中哪部分(或哪些部分)是 Goroutine?

同时在学习 WaitGroup。

我从 Go Playground 的 GoDoc 中复制并粘贴了这段代码。

https://play.golang.org/p/E8qTrnwvoue

我哪里做错了?

func (*WaitGroup) Add

func (wg *WaitGroup) Add(delta int)

Add 方法将 delta 值累加。

请解释一下这个添加 delta 的概念。


更多关于Golang中go routines的使用示例与问题讨论的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

如果打扰到大家,我表示抱歉。我该如何将此标记为已解决?如果您愿意,我可以自行研究。

更多关于Golang中go routines的使用示例与问题讨论的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


cherilexvold1974: 这段代码的哪一部分是 Goroutine?

Goroutine 是由 go 关键字创建的并发工作单元。它会创建一个调用给定函数的“工作者”。

cherilexvold1974: 我哪里做错了?

和另一个帖子里的情况一样,你把代码粘贴到了某个地方…… 它需要放在 main 函数体内,或者放在一个由 main 调用的函数里,才能看到效果。

cherilexvold1974: 请解释一下这个增加 delta 的概念。

它只是将你作为 delta 传入的值加到内部计数器上。你无法直接访问那个计数器,只能向它添加任意值(wg.Add(delta))、将计数器减 1wg.Done())或者等待它达到 0wg.Wait())。

因此,假设你有一个“全新”的 WaitGroup,然后执行 wg.Add(6) 并启动 goroutine,接着执行 wg.Wait(),那么至少需要有 6 个 goroutine 在该 WaitGroup 上调用 wg.Done(),才能使“父级”继续执行。

cherilexvold1974:

Will you demonstrate?

我要演示什么?演示代码需要放在函数里才不会出现语法错误吗?你已经提供了足够多的例子了……

演示函数需要被调用才能生效?这道理很简单,不需要例子……但既然你要求了:

The Go Play Space

Alternative Go (Golang) Playground with syntax highlighting, turtle graphics and more

就这段代码而言,你不会看到任何输出,但一旦你取消第8行的注释,让它变成 foo() 而不是 // foo(),你就会看到输出。就这么简单……

cherilexvold1974:

What’s delta?

整段话就是为了解释它……归根结底,它只是一个任意的名字,由Go标准库的开发者选择,用来表示一个参数,该参数代表与其先前值的差值。这符合科学中“delta”的用法。但是,它本可以叫 diffdifferencenx,甚至 fnoobrickl,尤其是后者没有任何解释性的上下文。

你不需要理解一个变量为什么叫这个名字,只需按照描述的方式使用它即可。

cherilexvold1974:

This whole thing is Greek to me.

抱歉,我无法解释得更简单了。

它的工作原理是这样的。WaitGroup维护着内部不可访问的状态,你可以向其中添加值,以1为步长递减,并等待其变为0

这已经是最简单的解释了。我认为,去研究它的实现对你没有帮助。

NobbZ:

和另一个帖子里的情况一样,你把代码粘贴到了某个地方……它需要放在 main 函数体内,或者放在一个会被 main 调用的函数里,才能看到效果。

你能演示一下吗?

NobbZ:

它只是把你作为 delta 传入的任何值加到内部计数器上。

NobbZ:

delta

delta 是什么?

来自 Quora… 1 个回答

Brian MacKay

Brian MacKay,在 Inntec 工作

更新于 2018年3月16日

Delta 出现在数学和各种科学中,它总是指代 变化量

当指代数据库时,delta 是一种学术性的描述方式,用来描述一个命令如何改变了数据。它可能指的是哪些行被删除了,或者单个行的旧值与新值的对比。

这不是大多数开发人员每天都需要考虑的事情,然而,每当我们使用数据库事务等功能时,我们都会从中受益。

事务允许我们对数据库进行一系列更改,但如果出现问题,可以将它们全部作为一个组取消。这可以防止我们的数据最终处于损坏或混乱的状态。

事务对我们来说很容易使用,但为了使该功能在数据库内部工作,一些非常聪明的人花费了他们职业生涯中相当多的时间来研究 delta。

这里有太多对我来说陌生的东西,我想我可能需要亲身体验才能理解。我打印了这个并做了书签,以便将来可以回来查看。

NobbZ:

它只是把你作为 delta 传入的任何值加到内部计数器上。你无法直接访问那个计数器,只能向它添加任意值(wg.Add(delta)),将计数器减 1wg.Done())或者等待它达到 0wg.Wait())。

所以假设你有一个“新”的 waitgroup,然后执行 wg.Add(6) 并启动 goroutine,接着执行 wg.Wait(),那么在

这整件事对我来说就像天书一样。

这段代码中,Goroutine 出现在以下部分:

  1. 第 20 行go func() { ... }() - 这是一个匿名函数作为 Goroutine 启动
  2. 第 26 行go worker(&wg) - 调用 worker 函数作为 Goroutine

关于 WaitGroup 的问题,第二个链接中的代码存在以下问题:

主要错误

  1. Add 方法调用位置错误:在第 18 行 wg.Add(1) 应该在启动 Goroutine 之前调用,而不是在 Goroutine 内部
  2. Wait 方法缺失:没有调用 wg.Wait() 来等待所有 Goroutine 完成

修正后的代码示例

package main

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

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done() // Goroutine 完成时通知 WaitGroup
    
    fmt.Printf("Worker %d starting\n", id)
    time.Sleep(time.Second)
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup
    
    for i := 1; i <= 3; i++ {
        wg.Add(1) // 在启动 Goroutine 前增加计数器
        go worker(i, &wg)
    }
    
    wg.Wait() // 等待所有 Goroutine 完成
    fmt.Println("All workers completed")
}

Add delta 概念解释Add(delta int) 方法用于增加或减少 WaitGroup 的计数器:

  • delta > 0:增加等待的 Goroutine 数量
  • delta < 0:减少等待的 Goroutine 数量
  • 计数器不能为负数,否则会 panic

正确使用模式

var wg sync.WaitGroup

// 启动前增加计数器
wg.Add(3) // 或 wg.Add(1) 在每个 Goroutine 启动前调用

go func() {
    defer wg.Done()
    // 任务逻辑
}()

go func() {
    defer wg.Done()
    // 任务逻辑
}()

go func() {
    defer wg.Done()
    // 任务逻辑
}()

wg.Wait() // 等待计数器归零

关键点

  1. Add() 必须在 Goroutine 启动前调用
  2. 每个 Goroutine 结束时必须调用 Done()(通常使用 defer
  3. Wait() 会阻塞直到计数器归零
  4. 不要重复使用 WaitGroup,完成一次等待后应创建新的实例
回到顶部