逐步学习Go-WaitGroup【连字都懒得写了,直接Show my Code】

news2024/11/18 16:39:10
package waitgroup_test

import (
    "fmt"
    "runtime"
    "sync"
    "testing"
    "time"

    "github.com/stretchr/testify/assert"
)

// 这是对Go语标准库中sync包下的WaitGroup的描述。

// WaitGroup用于等待一组并发的goroutine结结束。
// 主goroutine会调用Add方法来设置需要等待的goroutine的数量。
// 然后,每个goroutine在结束之后需要调用Done方法。
// 同时,可以使用Wait方法阻塞,直到所有的goroutine都结束。

// 在使用WaitGroup的过程中,一旦开始使用就不能再进行复制。

// 在 Go 内存模型的术语中,对 Done 的调用 “在” 它解除的任何 Wait 调用返回前“同步”。
// 这是一种保证内存可见性的机制,即一旦Done被调用,Wait就能获知并返回。
// 在并发编程中,这种机制能确保相互依赖的操作的正确顺序,避免了潜在的竞态条件。

// [很重要]如果你想复用一个WaitGroup,等待几组不相关的事件,你必须确保在调用新一轮的Add之前,所有先前的Wait调用都已经返回完毕。

// TestWaitGroup_ShouldComplete_WhenAddCountEqualsDoneCount 测试了一个正常的
// WaitGroup 使用场景。我们添加了一个需要等待的 goroutine,当这个 goroutine 完成时,
// WaitGroup 的 Wait 方法应该返回。这是最基础的使用 WaitGroup 的场景。
func TestWaitGroup_ShouldComplete_WhenAddCountEqualsDoneCount(t *testing.T) {
    wg := sync.WaitGroup{}

    wg.Add(1)
    go func() {
        println("Hello WaitGroup")
        wg.Done()
    }()

    wg.Wait()

    completed := 1
    assert.Equal(t, 1, completed)
}

// TestWaitGroup_ShouldCompleted_WhenMultipleGoroutineDoNormalAddAndDone 用于测试多个
// 并发的 Goroutine 是否能够正确地被 WaitGroup 跟踪以及同步。
// 我们创建了 loopCount 个并发的 Goroutine,并且每个 Goroutine 会在完成任务后调用 Done 方法标记。
// 我们在主 Goroutine 中 阻塞等待所有的 Goroutine 完成。
// 如果所有的 goroutine 都能顺利地完成数据处理且调用了Done 方法,WaitGroup 的 Wait 方法将会顺利返回,解除阻塞并且继续执行。
func TestWaitGroup_ShouldCompleted_WhenMultipleGoroutineDoNormalAddAndDone(t *testing.T) {
    wg := sync.WaitGroup{}

    loopCount := 100

    wg.Add(loopCount)

    for i := 0; i < loopCount; i++ {
        go func(i int) {
            println("executed ", i, "")
            wg.Done()
        }(i)
    }

    isSuccess := true

    wg.Wait()

    assert.True(t, isSuccess)

}

// TestWaitGroup_ShouldCompleted_WhenReuseWaitGroupAfterWaitReturnedAtFirstRound 用于测试在 WaitGroup 对象的 Wait 方法返回后,
// 是否可以再次复用 WaitGroup 对象来等待新的并发的 Goroutine。
// 首先,我们创建了 loopCount 个并发的 Goroutine,每个 Goroutine 完成任务之后都会调用 Done 方法。
// 在主 Goroutine 中我们调用 Wait 方法来阻塞等待这一组 Goroutine 的完成。
// 如果所有的 Goroutine 完成任务并调用了 Done 方法,那么 Wait 方法应该能够返回,说明第一轮的并发操作成功完成。
// 在第一轮的并发操作完成后,我们再次复用了这个 WaitGroup 对象,再次添加 loopCount 个并发的 Goroutine 并在主 Goroutine 中调用 Wait 方法阻塞等待。
// 如果这一轮的并发操作也能够成功完成,那么说明 WaitGroup 对象在 Wait 方法返回后可以被再次复用。
func TestWaitGroup_ShouldCompleted_WhenReuseWaitGroupAfterWaitReturnedAtFirstRound(t *testing.T) {
    wg := sync.WaitGroup{}

    loopCount := 100

    // 第一轮
    wg.Add(loopCount)

    for i := 0; i < loopCount; i++ {
        go func(i int) {
            println("executed ", i, "")
            wg.Done()
        }(i)
    }

    isFirstRoundSuccess := false
    wg.Wait()
    isFirstRoundSuccess = true

    // 第二轮
    wg.Add(loopCount)

    for i := 0; i < loopCount; i++ {
        go func(i int) {
            println("executed ", i, "")
            wg.Done()
        }(i)
    }

    isNextRoundSuccess := false

    wg.Wait()

    isNextRoundSuccess = true

    assert.True(t, isFirstRoundSuccess)
    assert.True(t, isNextRoundSuccess)
}

// TestWaitGroup_ShouldAllReturned_WhenMultipleGoroutineWaitForDone 测试了多个Goroutine
// 在同一时刻等待一个WaitGroup。在这个测试中,添加了一个Goroutine到WaitGroup并且该Goroutine会在
// 一段时间后调用Done()方法。而在其他的Goroutine中,它们通过调用Wait()方法等待这个WaitGroup。
// 所有等待该WaitGroup的Goroutine都应该在Done被调用后解锁。如果所有的Goroutine都顺利解锁并且Wait方法返回,
// 则表明WaitGroup可以正确地等待所有的Goroutine完成。
func TestWaitGroup_ShouldAllReturned_WhenMultipleGoroutineWaitForDone(t *testing.T) {
    wg := sync.WaitGroup{}

    wg.Add(1)
    go func() {
        time.Sleep(time.Second)
        wg.Done()
        println("Done")

    }()

    go func() {
        wg.Wait()
        println("Wait complete 1")
    }()

    go func() {
        wg.Wait()
        println("Wait complete 2")
    }()
    isSuccess := false
    wg.Wait()
    isSuccess = true

    assert.True(t, isSuccess)
}

// TestWaitGroup_ShouldBlocked_WhenAddCountGreaterThanDoneCount  测试了一种特殊情况
// 当我们添加了更多的需要等待的 goroutine 比实际完成的 goroutine 时,WaitGroup 的 Wait
// 方法应该阻塞,直至等待的 goroutine 完成。在这个测试用例中,我们专门添加了一个超时
// 机制来判断 Wait 是否正常阻塞。
func TestWaitGroup_ShouldBlocked_WhenAddCountGreaterThanDoneCount(t *testing.T) {
    wg := sync.WaitGroup{}
    ch := make(chan bool)

    wg.Add(2)
    go func() {
        println("Hello WaitGroup")
        wg.Done()
    }()

    go func() {
        wg.Wait()
        ch <- true
    }()

    isCompleted := false
    isTimeout := false
    select {
    case <-ch:
        isCompleted = true
    case <-time.After(10 * time.Second):
        isTimeout = true
    }

    assert.False(t, isCompleted)
    assert.True(t, isTimeout)
}

// TestWaitGroup_ShouldPanic_WhenAddNegtiveCounterWithoutAddPositiveCounter 在添加负数
// goroutine 时,WaitGroup 应该 panic。这是因为添加负数的 goroutine 没有任何意义,反而
// 会导致等待的 goroutine 数量变为负数,这是一种错误的使用方式。
func TestWaitGroup_ShouldPanic_WhenAddNegtiveCounterWithoutAddPositiveCounter(t *testing.T) {
    wg := sync.WaitGroup{}

    assert.Panics(t, func() { wg.Add(-1) })
}

// TestWaitGroup_ShouldPainic_WhenTryToReusePreviousWaitHasReturned 当我们尝试在上一个
// Wait 调用还没有返回时,复用 WaitGroup,WaitGroup 应该 panic。
// 这是因为如果我们在上一个Wait 调用没有返回之前就复用 WaitGroup,那么新增的 goroutine 可能会影响上一个 Wait 调用
// 的正确返回结果。
func TestWaitGroup_ShouldPainic_WhenTryToReusePreviousWaitHasReturned(t *testing.T) {
    var wg sync.WaitGroup

    runtime.GOMAXPROCS(3)

    wg.Add(1)

    go func() {
        fmt.Println("Wait executed")
        wg.Wait()
        fmt.Println("Wait completed")
    }()

    go func() {
        time.Sleep(time.Second * 5)
        wg.Done()
        fmt.Println("Add executed")
        wg.Add(1)
    }()

    go func() {
        time.Sleep(time.Second * 5)
        fmt.Println("Done executed")
        wg.Done()
    }()

    time.Sleep(100 * time.Second)
}

// TestWaitGroup_ShouldPanic_WhenNoAddCalled 用于测试在没有通过 Add 方法添加任何需要
// 等待的 Goroutine 的情况下,直接调用 Done 方法是否会抛出 panic。
// 根据 WaitGroup 的使用规则,我们必须显式地通过调用 Add 方法告诉 WaitGroup,有多少个 Goroutine 需要等待完成。
// 如果我们没有通过 Add 方法添加任何需要等待的 Goroutine,而直接调用 Done 方法,那么应该
// 会抛出 panic,因为 Done 方法预期会有一些任务需要它去完成,然而这些任务并未被正确添加到
// WaitGroup 中。
func TestWaitGroup_ShouldPanic_WhenNoAddCalled(t *testing.T) {
    wg := sync.WaitGroup{}

    assert.Panics(t, func() {
        wg.Done()
    })

}

COPY

倒数第二个测试用例的截图:

file

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1570948.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

争相上市,黑芝麻智能和地平线,能突出重围吗?

图片&#xff5c;freeflo.ai ©自象限原创 作者丨罗辑 中国最有代表性两家自动驾驶大算力芯片&#xff08;SoC&#xff09;公司在港交所相遇了。 3月23日&#xff0c;黑芝麻智能向港交所递交主板上市申请&#xff1b;3天之后&#xff0c;地平线也向港交所递交了招股书。…

基于Springboot的少儿编程管理系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的少儿编程管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构…

二极管特性介绍

二极管 贴片二极管 不同封装的二极管 二极管不同的符号 二极管的结构组成 二极管就是由一个PN结加上相应的电极引线及管壳封装而成的。 采用不同的掺杂工艺&#xff0c;通过扩散作用&#xff0c;将P型半导体与N型半导体制作在同一块半导体&#xff08;通常是硅或锗&#xff…

【前端面试3+1】08 css选择器、在前端页面展示后端传来的图片数组、请求方法的常见类型、【搜索插入位置】

一、css选择器有哪些&#xff1f; 1.元素选择器&#xff1a; 通过元素名称选择元素。 示例&#xff1a;p 选择所有段落元素。 2.类选择器&#xff1a; 通过类名选择元素。 示例&#xff1a;.btn 选择所有类名为 btn 的元素。 3.ID选择器&#xff1a; 通过id属性选择元素。 示例…

Adobe Bridge 2024:连接创意,探索无限可能 mac/win版

Adobe Bridge 2024&#xff0c;作为Adobe家族中的一款强大的创意管理工具&#xff0c;再次革新了数字资产管理和工作流程优化的标准。这款软件不仅继承了Adobe Bridge一贯的直观界面和强大功能&#xff0c;更在多个方面进行了突破性的改进。 Bridge 2024软件获取 全面的资源管…

软考之零碎片段记录(六)+复习巩固

A. 上新 一、关系模式 1. 决定属性 AB->C,函数依赖左侧出现为决定属性 AB->C,函数依赖右侧出现为非决定属性 候选键在决定属性中挑选&#xff0c;AB->C, CD->B中&#xff0c;A,D为侯选建 二、授权SQL 将权限授予用户&#xff08;grant <权限> on&#xf…

Git安装教程(图文安装)

Git Bash是git(版本管理器)中提供的一个命令行工具&#xff0c;外观类似于Windows系统内置的cmd命令行工具。 可以将Git Bash看作是一个终端模拟器&#xff0c;它提供了类似于Linux和Unix系统下Bash Shell环境的功能。通过Git Bash&#xff0c;用户可以在Windows系统中运行基于…

【个人笔记】如何用 Python 编写激活码解锁程序,方法二

目录 前言 第一步&#xff1a;编写激活码解锁程序&#xff08;激活码.py&#xff09; 第二步&#xff1a;修改需要解锁的程序&#xff08;1.py&#xff09; 总结 前言 在软件开发中&#xff0c;有时我们需要设计一种机制来保护程序&#xff0c;例如通过激活码来控制程序的…

【MySQL】:深入解析多表查询(上)

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; MySQL从入门到进阶 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一. 多表关系1.1 一对多1.2 多对多1.3 一对一 二. 多表查询概述2.1 概述2.2 分类…

C51实现每秒向电脑发送数据(UART的含义)

其实核心的问题是&#xff1a;串口的通信方式 异步串行是指UART&#xff08;Universal Asynchronous Receiver/Transmitter&#xff09;&#xff0c;UART包含TTL电平的串口和RS232电平的串口 UART要实现异步通信的&#xff1a; UART是异步串行接口&#xff0c;通信双方使用时…

2024唐山国际门窗幕墙展览会

2024唐山国际门窗幕墙展览会 2024TangshanInternational Door and Window Curtain Wall Exhibition 2024年6月14-16日 地点&#xff1a;唐山南湖国际会展中心 唐山国际门窗幕墙博览会一 年一届&#xff0c;深耕京津冀核心区域&#xff0c;专注门窗行业高质量 发展&#x…

网络协议——HTTP协议

目录 ​编辑 一&#xff0c;HTTP协议基本认识 二&#xff0c;认识URL 三&#xff0c;http协议的格式 1&#xff0c;发送格式 2&#xff0c;回应格式 四&#xff0c;服务端代码 五&#xff0c;http报文细节 1&#xff0c;Post与Get方法 2&#xff0c;Content_lenth 3&…

基于Springboot的航班进出港管理系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的航班进出港管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结…

C++的 stack和queue 的应用和实现【双端队列的理解和应用】

文章目录 stack的理解和应用栈的理解栈的模拟实现string实现stackvector实现stack queue的理解和应用队列的理解队列的模拟实现 双端队列原理的简单理解deque的缺陷为什么选择deque作为stack和queue的底层默认容器STL标准库中对于stack和queue的模拟实现stack的模拟实现queue的…

智能停车场物联网远程监控解决方案

智能停车场物联网远程监控解决方案 智能停车场物联网远程监控解决方案是一种集成了现代物联网技术、大数据分析以及云计算等先进技术手段&#xff0c;对停车场进行全面智能化管理的综合系统。它通过实时感知、精准采集和高效传输各类停车数据&#xff0c;实现对停车场运营状态…

基于Springboot的学校防疫物资管理平台(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的学校防疫物资管理平台&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系…

Data-efficient Fine-tuning for LLM-based Recommendation

目录 Introduction 利用大型语言模型&#xff08;LLM&#xff09;进行推荐最近引起了相当大的关注&#xff0c;其中微调在 LLM 的适应中发挥着关键作用。然而&#xff0c;在快速扩展的推荐数据上微调LLMs的成本限制了其实际应用。为了应对这一挑战&#xff0c;小样本微调提供了…

RabbitMQ3.13.x之十_流过滤的内部结构设计与实现

RabbitMQ3.13.x之十_流过滤的内部结构设计与实现 文章目录 RabbitMQ3.13.x之十_流过滤的内部结构设计与实现1. 概念1. 消息发布2. 消息消费 2. 流的结构1. 在代理端进行过滤2. 客户端筛选3. JavaAPI示例4. 流过滤配置5. AMQP上的流过滤6. 总结 3. 相关链接 1. 概念 流过滤的思…

linux练习-交互式传参

在shell脚本中&#xff0c;read 向用户显示一行文本并接受用户输入 #!/bin/bash read -p 依次输入你的姓名、年龄、家乡 name age home echo 我是$name,年龄$age,我来自$home

C++数据结构与算法——二叉树公共祖先问题

C第二阶段——数据结构和算法&#xff0c;之前学过一点点数据结构&#xff0c;当时是基于Python来学习的&#xff0c;现在基于C查漏补缺&#xff0c;尤其是树的部分。这一部分计划一个月&#xff0c;主要利用代码随想录来学习&#xff0c;刷题使用力扣网站&#xff0c;不定时更…