跳转到主要内容

标签(标签)

资源精选(342) Go开发(108) Go语言(103) Go(99) angular(82) LLM(75) 大语言模型(63) 人工智能(53) 前端开发(50) LangChain(43) golang(43) 机器学习(39) Go工程师(38) Go程序员(38) Go开发者(36) React(33) Go基础(29) Python(24) Vue(22) Web开发(20) Web技术(19) 精选资源(19) 深度学习(19) Java(18) ChatGTP(17) Cookie(16) android(16) 前端框架(13) JavaScript(13) Next.js(12) 安卓(11) 聊天机器人(10) typescript(10) 资料精选(10) NLP(10) 第三方Cookie(9) Redwoodjs(9) LLMOps(9) Go语言中级开发(9) 自然语言处理(9) PostgreSQL(9) 区块链(9) mlops(9) 安全(9) 全栈开发(8) ChatGPT(8) OpenAI(8) Linux(8) AI(8) GraphQL(8) iOS(8) 软件架构(7) Go语言高级开发(7) AWS(7) C++(7) 数据科学(7) whisper(6) Prisma(6) 隐私保护(6) RAG(6) JSON(6) DevOps(6) 数据可视化(6) wasm(6) 计算机视觉(6) 算法(6) Rust(6) 微服务(6) 隐私沙盒(5) FedCM(5) 语音识别(5) Angular开发(5) 快速应用开发(5) 提示工程(5) Agent(5) LLaMA(5) 低代码开发(5) Go测试(5) gorm(5) REST API(5) 推荐系统(5) WebAssembly(5) GameDev(5) CMS(5) CSS(5) machine-learning(5) 机器人(5) 游戏开发(5) Blockchain(5) Web安全(5) Kotlin(5) 低代码平台(5) 机器学习资源(5) Go资源(5) Nodejs(5) PHP(5) Swift(5) 智能体(4) devin(4) Blitz(4) javascript框架(4) Redwood(4) GDPR(4) 生成式人工智能(4) Angular16(4) Alpaca(4) 编程语言(4) SAML(4) JWT(4) JSON处理(4) Go并发(4) kafka(4) 移动开发(4) 移动应用(4) security(4) 隐私(4) spring-boot(4) 物联网(4) nextjs(4) 网络安全(4) API(4) Ruby(4) 信息安全(4) flutter(4) 专家智能体(3) Chrome(3) CHIPS(3) 3PC(3) SSE(3) 人工智能软件工程师(3) LLM Agent(3) Remix(3) Ubuntu(3) GPT4All(3) 软件开发(3) 问答系统(3) 开发工具(3) 最佳实践(3) RxJS(3) SSR(3) Node.js(3) Dolly(3) 移动应用开发(3) 低代码(3) IAM(3) Web框架(3) CORS(3) 基准测试(3) Go语言数据库开发(3) Oauth2(3) 并发(3) 主题(3) Theme(3) earth(3) nginx(3) 软件工程(3) azure(3) keycloak(3) 生产力工具(3) gpt3(3) 工作流(3) C(3) jupyter(3) 认证(3) prometheus(3) GAN(3) Spring(3) 逆向工程(3) 应用安全(3) Docker(3) Django(3) R(3) .NET(3) 大数据(3) Hacking(3) 渗透测试(3) C++资源(3) Mac(3) 微信小程序(3) Python资源(3) JHipster(3) 大型语言模型(2) 语言模型(2) 可穿戴设备(2) JDK(2) SQL(2) Apache(2) Hashicorp Vault(2) Spring Cloud Vault(2) Go语言Web开发(2) Go测试工程师(2) WebSocket(2) 容器化(2) AES(2) 加密(2) 输入验证(2) ORM(2) Fiber(2) Postgres(2) Gorilla Mux(2) Go数据库开发(2) 模块(2) 泛型(2) 指针(2) HTTP(2) PostgreSQL开发(2) Vault(2) K8s(2) Spring boot(2) R语言(2) 深度学习资源(2) 半监督学习(2) semi-supervised-learning(2) architecture(2) 普罗米修斯(2) 嵌入模型(2) productivity(2) 编码(2) Qt(2) 前端(2) Rust语言(2) NeRF(2) 神经辐射场(2) 元宇宙(2) CPP(2) 数据分析(2) spark(2) 流处理(2) Ionic(2) 人体姿势估计(2) human-pose-estimation(2) 视频处理(2) deep-learning(2) kotlin语言(2) kotlin开发(2) burp(2) Chatbot(2) npm(2) quantum(2) OCR(2) 游戏(2) game(2) 内容管理系统(2) MySQL(2) python-books(2) pentest(2) opengl(2) IDE(2) 漏洞赏金(2) Web(2) 知识图谱(2) PyTorch(2) 数据库(2) reverse-engineering(2) 数据工程(2) swift开发(2) rest(2) robotics(2) ios-animation(2) 知识蒸馏(2) 安卓开发(2) nestjs(2) solidity(2) 爬虫(2) 面试(2) 容器(2) C++精选(2) 人工智能资源(2) Machine Learning(2) 备忘单(2) 编程书籍(2) angular资源(2) 速查表(2) cheatsheets(2) SecOps(2) mlops资源(2) R资源(2) DDD(2) 架构设计模式(2) 量化(2) Hacking资源(2) 强化学习(2) flask(2) 设计(2) 性能(2) Sysadmin(2) 系统管理员(2) Java资源(2) 机器学习精选(2) android资源(2) android-UI(2) Mac资源(2) iOS资源(2) Vue资源(2) flutter资源(2) JavaScript精选(2) JavaScript资源(2) Rust开发(2) deeplearning(2) RAD(2)

如果您刚刚开始学习 Go 以及如何实现高并发、高性能应用程序,那么了解 WaitGroups 至关重要。

在本教程中,我们将介绍以下内容:

  • WaitGroups 是什么以及我们应该在什么时候使用它们
  • 使用 WaitGroups 的简单示例
  • WaitGroups 的真实示例

到此结束时,您应该对如何在您自己的并发 Go 应用程序中使用 WaitGroups 有一个坚实的掌握。

注意 - 本教程的完整代码可以在这里找到:TutorialEdge/go-waitgroup-tutorial

视频教程

https://youtu.be/0BPSR-W4GSY

了解等待组


让我们直接深入了解什么是 WaitGroup 以及它为我们解决了什么问题。

当您开始在 Go 中编写利用 goroutines 的应用程序时,您会遇到需要阻止代码库某些部分执行的场景,直到这些 goroutines 成功执行。

以这段代码为例:

package main

import "fmt"

func myFunc() {
    fmt.Println("Inside my goroutine")
}

func main() {
    fmt.Println("Hello World")
    go myFunc()
    fmt.Println("Finished Execution")
}

它在触发 goroutine 之前首先打印出 Hello World,最后打印出 Finished Execution。

然而,当你运行这个程序时,你应该注意到,当你运行这个程序时,它没有到达第 6 行,并且它从来没有真正打印出 Inside my goroutine。这是因为 main 函数实际上在 goroutine 有机会执行之前就终止了。

解决方案? - 等待组


WaitGroups 本质上允许我们通过阻塞直到 WaitGroup 中的任何 goroutine 成功执行来解决这个问题。

我们首先在 WaitGroup 上调用 .Add(1) 来设置我们想要等待的 goroutine 的数量,然后,我们在任何 goroutine 中调用 .Done() 来表示其执行结束。

注意 - 您需要确保在执行 goroutine 之前调用 .Add(1)。

一个简单的例子


现在我们已经介绍了基本理论,让我们看看如何通过使用 WaitGroups 来修复前面的示例:

package main

import (
    "fmt"
    "sync"
)

func myFunc(waitgroup *sync.WaitGroup) {
    fmt.Println("Inside my goroutine")
    waitgroup.Done()
}

func main() {
    fmt.Println("Hello World")

    var waitgroup sync.WaitGroup
    waitgroup.Add(1)
    go myFunc(&waitgroup)
    waitgroup.Wait()

    fmt.Println("Finished Execution")
}

如您所见,我们已经实例化了一个新的 sync.WaitGroup,然后调用 .Add(1) 方法,然后再尝试执行我们的 goroutine。

我们更新了函数以接收指向现有 sync.WaitGroup 的指针,然后在成功完成任务后调用 .Done() 方法。

最后,在第 19 行,我们调用 waitgroup.Wait() 来阻止 main() 函数的执行,直到 waitgroup 中的 goroutine 成功完成。

当我们运行这个程序时,我们现在应该看到以下输出:

$ go run main.go
Hello World
Inside my goroutine
Finished Execution

匿名函数


应该注意的是,如果我们愿意,我们可以使用匿名函数完成与上述相同的事情。如果 goroutine 本身不太复杂,这会更简洁,更容易阅读:

package main

import (
    "fmt"
    "sync"
)

func main() {
    fmt.Println("Hello World")

    var waitgroup sync.WaitGroup
    waitgroup.Add(1)
    go func() {
        fmt.Println("Inside my goroutine")
        waitgroup.Done()
    }()
    waitgroup.Wait()

    fmt.Println("Finished Execution")
}

同样,如果我们运行它,它会提供相同的结果:

$ go run main.go
Hello World
Inside my goroutine
Finished Execution


但是,当我们需要在函数中输入参数时,当我们这样做时,事情开始变得有点复杂。

例如,我们想将一个 URL 传递给我们的函数,我们必须像这样传递那个 URL:

go func(url string) {
  fmt.Println(url)
}(url)


如果您遇到此问题,请记住这一点。

一个“真实”世界的例子


在我的一个生产应用程序中,我的任务是创建一个与大量其他 API 交互的 API,并将结果汇​​总到一个响应中。

这些 API 调用中的每一个都需要大约 2-3 秒才能返回响应,并且由于我必须进行大量 API 调用,因此同步执行此操作是不可能的。

为了使这个端点可用,我必须使用 goroutine 并异步执行这些请求。

package main

import (
    "fmt"
    "log"
    "net/http"
)

var urls = []string{
    "https://google.com",
    "https://tutorialedge.net",
    "https://twitter.com",
}

func fetch(url string) {
    resp, err := http.Get(url)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(resp.Status)
}

func homePage(w http.ResponseWriter, r *http.Request) {
    fmt.Println("HomePage Endpoint Hit")
    for _, url := range urls {
        go fetch(url)
    }
    fmt.Println("Returning Response")
    fmt.Fprintf(w, "All Responses Received")
}

func handleRequests() {
    http.HandleFunc("/", homePage)
    log.Fatal(http.ListenAndServe(":8081", nil))
}

func main() {
    handleRequests()
}

然而,当我第一次使用这种策略时,我注意到我对 API 端点的任何调用都在我的 goroutine 有机会完成并填充结果之前返回。

这是我了解 WaitGroups 并深入了解同步包的地方。

通过使用 WaitGroup,我可以有效地修复这种意外行为,并且仅在我的所有 goroutine 完成后才返回结果。

package main

import (
    "fmt"
    "log"
    "net/http"
    "sync"
)

var urls = []string{
    "https://google.com",
    "https://tutorialedge.net",
    "https://twitter.com",
}

func fetch(url string, wg *sync.WaitGroup) (string, error) {
    resp, err := http.Get(url)
    if err != nil {
        fmt.Println(err)
        return "", err
    }
    wg.Done()
    fmt.Println(resp.Status)
    return resp.Status, nil
}

func homePage(w http.ResponseWriter, r *http.Request) {
    fmt.Println("HomePage Endpoint Hit")
    var wg sync.WaitGroup

    for _, url := range urls {
        wg.Add(1)
        go fetch(url, &wg)
    }

    wg.Wait()
    fmt.Println("Returning Response")
    fmt.Fprintf(w, "Responses")
}

func handleRequests() {
    http.HandleFunc("/", homePage)
    log.Fatal(http.ListenAndServe(":8081", nil))
}

func main() {
    handleRequests()
}

现在我已将 WaitGroup 添加到此端点,它将对列出的所有 URL 执行 HTTP GET 请求,并且只有在完成后,才会向调用该特定端点的客户端返回响应。

$ go run wg.go
HomePage Endpoint Hit
200 OK
200 OK
200 OK
Returning Response


注意 - 解决此问题的方法不止一种,通过使用渠道可以实现类似的有效结果。 有关频道的更多信息 - Go Channels 教程

结论


在本教程中,我们学习了 WaitGroup 的基础知识,包括它们是什么以及我们如何在我们自己的 Go 高性能应用程序中使用它们。

如果您喜欢本教程或有任何意见/建议,请随时在下面的评论部分或旁边的建议部分告诉我!

 

文章链接