go数据处理之textproto.Pipeline
Pipeline类的奇妙效果
发现一个很有意思的类textproto.Pipeline
, 这个类可以让我们定义函数执行的顺序。
比如,我定义了一个testPrintln
函数,功能是向控制台打印,当我输入了:
testPrintln("3")
testPrintln("1")
testPrintln("2")
testPrintln("0")
testPrintln("4")
但是结果却是:
0
1
2
3
4
Pipeline类的简介
-
在 Go 语言的 net/textproto 包中,Pipeline 是一个用于协调并发读写文本协议的同步工具,主要解决多个 goroutine 并发发送请求时,响应与请求的顺序匹配问题。它常用于基于文本的协议(如 HTTP、SMTP、FTP 等)的客户端实现中。
-
当多个 goroutine 同时通过同一个连接发送请求时,可能出现 “请求乱序” 或 “响应与请求不匹配” 的问题(例如,goroutine A 发送的请求,其响应可能被 goroutine B 错误接收)。Pipeline 通过内部同步机制,确保:
- 响应的接收顺序与请求的发送顺序严格一致;
- 每个请求的响应能被对应的 goroutine 正确接收。
核心方法
- Pipeline 结构体仅暴露两个核心方法,用于标记请求的生命周期:
StartRequest()
- 无参数、无返回值。
- 调用时机:在发送请求前调用,标记一个新请求的开始。
- 内部逻辑:通过互斥锁增加内部计数器,记录当前活跃请求的数量。
EndRequest()
- 无参数、无返回值。
- 调用时机:在请求处理完成(通常是收到响应后)调用,标记请求的结束。
- 内部逻辑:减少内部计数器,并唤醒等待的读取操作,确保下一个响应能被正确处理。
工作原理
Pipeline
内部通过计数器和等待队列实现同步:
- 维护一个计数器 n,记录当前未完成的请求数量(StartRequest 增 1,EndRequest 减 1)。
- 当读取响应时,Pipeline 会检查计数器,确保只有前序请求都已完成(n 递减到对应值),当前响应才能被读取。
- 这种机制强制响应按 “请求发送顺序” 被接收,即使多个 goroutine 并发操作,也不会出现响应错乱。
示例代码
package mainimport ("fmt""net/textproto""time"
)func testRequest(id uint, pipeline *textproto.Pipeline) {pipeline.StartRequest(id)fmt.Println("request: ", id)defer pipeline.EndRequest(id)
}func testResponse(id uint, pipeline *textproto.Pipeline) {pipeline.StartResponse(id)fmt.Println("response: ", id)defer pipeline.EndResponse(id)
}
func main() {pipeline := textproto.Pipeline{}go testRequest(3, &pipeline)go testRequest(1, &pipeline)go testRequest(2, &pipeline)go testRequest(0, &pipeline)go testRequest(4, &pipeline)time.Sleep(5 * time.Second)go testResponse(3, &pipeline)go testResponse(1, &pipeline)go testResponse(2, &pipeline)go testResponse(0, &pipeline)go testResponse(4, &pipeline)time.Sleep(5 * time.Second)
}