golang并发测试辅助工具插件库stop-and-go的使用
stop-and-go
并发测试辅助工具库
安装
go get -u github.com/elgohr/stop-and-go
使用示例
下面是一个完整的示例,展示如何使用stop-and-go进行并发测试:
package main
import (
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/elgohr/stop-and-go/constraint"
"github.com/elgohr/stop-and-go/wait"
)
func TestExample(t *testing.T) {
// 创建三个等待器,每个等待器设置1秒超时
w1 := wait.NewWaiter(time.Second)
w2 := wait.NewWaiter(time.Second)
w3 := wait.NewWaiter(time.Second)
// 创建一个测试HTTP服务器
ts1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w2.Done() // 请求处理完成后调用Done
}))
defer ts1.Close()
// 启动一个goroutine,立即完成w3
go func() {
w3.Done()
}()
// 启动另一个goroutine发送HTTP请求
go func() {
if _, err := http.Get(ts1.URL); err != nil {
t.Error(err)
}
w1.Done() // 请求发送完成后调用Done
}()
// 设置测试约束条件:
// 1. w3可以以任意顺序完成
// 2. w1必须在w2之前完成
if err := wait.For(
constraint.NoOrder(w3),
constraint.Before(w1, w2),
); err != nil {
t.Error(err)
}
}
代码说明
- 首先创建了三个等待器(w1, w2, w3),每个等待器设置1秒超时
- 创建了一个测试HTTP服务器,在处理请求时会调用w2.Done()
- 启动两个goroutine:
- 一个立即完成w3
- 另一个发送HTTP请求并在完成后完成w1
- 使用wait.For设置测试约束:
- w3可以以任意顺序完成(constraint.NoOrder)
- w1必须在w2之前完成(constraint.Before)
这个示例展示了如何使用stop-and-go来测试并发操作的执行顺序是否符合预期。
更多关于golang并发测试辅助工具插件库stop-and-go的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang并发测试辅助工具插件库stop-and-go的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang并发测试辅助工具stop-and-go使用指南
stop-and-go
是一个轻量级的Go语言并发测试辅助工具库,主要用于控制goroutine的启动和停止,特别适合在并发测试场景中使用。
主要功能
- 提供简单的同步机制来控制goroutine的启动
- 可以等待所有goroutine完成
- 简化并发测试的编写
安装
go get github.com/yourusername/stop-and-go
基本用法
1. 基本示例
package main
import (
"fmt"
"time"
"github.com/yourusername/stop-and-go"
)
func worker(id int, sg *stopandgo.StopAndGo) {
fmt.Printf("Worker %d waiting to start\n", id)
sg.Start() // 等待开始信号
defer sg.Done() // 标记完成
fmt.Printf("Worker %d started\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d finished\n", id)
}
func main() {
sg := stopandgo.New()
// 启动5个worker
for i := 0; i < 5; i++ {
go worker(i, sg)
}
fmt.Println("Main: Starting workers...")
sg.Release() // 释放所有等待的goroutine
fmt.Println("Main: Waiting for workers to finish...")
sg.Wait() // 等待所有goroutine完成
fmt.Println("Main: All workers finished")
}
2. 并发测试示例
package main_test
import (
"testing"
"time"
"github.com/yourusername/stop-and-go"
)
func TestConcurrentOperations(t *testing.T) {
sg := stopandgo.New()
var counter int
// 启动10个goroutine增加计数器
for i := 0; i < 10; i++ {
go func() {
sg.Start()
defer sg.Done()
// 临界区操作
counter++
}()
}
// 确保所有goroutine都已准备好
time.Sleep(100 * time.Millisecond)
// 释放所有goroutine
sg.Release()
// 等待所有goroutine完成
sg.Wait()
if counter != 10 {
t.Errorf("Expected counter to be 10, got %d", counter)
}
}
高级用法
1. 带超时的等待
func TestWithTimeout(t *testing.T) {
sg := stopandgo.New()
go func() {
sg.Start()
defer sg.Done()
time.Sleep(2 * time.Second) // 模拟长时间运行的任务
}()
sg.Release()
// 等待最多1秒
if !sg.WaitTimeout(1 * time.Second) {
t.Error("Timeout waiting for goroutines to finish")
}
}
2. 多次使用StopAndGo
func TestMultiplePhases(t *testing.T) {
sg1 := stopandgo.New()
sg2 := stopandgo.New()
var phase1, phase2 int
// 启动goroutine
for i := 0; i < 5; i++ {
go func(id int) {
sg1.Start()
phase1++
sg1.Done()
sg2.Start()
phase2++
sg2.Done()
}(i)
}
// 释放第一阶段
sg1.Release()
sg1.Wait()
if phase1 != 5 {
t.Errorf("Phase1: expected 5, got %d", phase1)
}
// 释放第二阶段
sg2.Release()
sg2.Wait()
if phase2 != 5 {
t.Errorf("Phase2: expected 5, got %d", phase2)
}
}
实现原理
stop-and-go
的核心是基于sync.WaitGroup
和通道实现的。主要组件包括:
Start()
方法:goroutine调用此方法等待开始信号Release()
方法:主goroutine调用此方法释放所有等待的goroutineDone()
方法:goroutine完成任务后调用Wait()
方法:主goroutine等待所有goroutine完成
注意事项
- 确保每个
Start()
都有对应的Done()
调用,否则Wait()
会永远阻塞 Release()
只需调用一次,多次调用不会有额外效果- 适用于测试场景,生产环境请根据需求评估使用
替代方案
如果stop-and-go
不能满足需求,也可以考虑:
sync.WaitGroup
:Go标准库提供的基本同步原语errgroup.Group
:支持错误传播的goroutine组golang.org/x/sync/semaphore
:加权信号量
stop-and-go
的优势在于它提供了更简单的接口来专门解决"等待所有goroutine准备好后同时开始"这一特定场景的需求。
希望这个指南能帮助你有效地使用stop-and-go
进行并发测试!