在Go语言里,链式调用只是把多个方法挨个返回对象继续调用,语法上连续,执行上仍是顺序的。一旦把它放进Goroutine,整个调用链就作为一个并发任务运行,理解它的机制和同步方式对写稳并发程序很关键。

链式调用在Goroutine中的执行机制
下面这段代码把链式调用放进了Goroutine,从输出可以看到,所有方法都在新Goroutine里按顺序执行,主Goroutine不会等它。
package main
import (
"fmt"
"time"
)
type Builder struct {
name string
}
func (b Builder) Step1() Builder {
fmt.Println("step1", b.name)
return b
}
func (b Builder) Step2() Builder {
fmt.Println("step2", b.name)
return b
}
func main() {
go func() {
Builder{name: "task"}.Step1().Step2()
}()
time.Sleep(time.Millisecond * 100)
}
上面例子中,Builder{name:"task"}.Step1().Step2() 在Goroutine里从头跑到尾,主函数靠Sleep等它结束,这很明显不是正规同步方式。
用Channel做同步与结果传递
更合理的做法是利用Channel等Goroutine完成任务并取回结果,避免靠Sleep硬等。
package main
import "fmt"
type Result struct {
data string
}
type Chain struct {
val string
}
func (c Chain) Add(s string) Chain {
c.val = c.val + s
return c
}
func runChain(ch chan Result) {
r := Chain{}.Add("a").Add("b").Add("c")
ch <- Result{data: r.val}
}
func main() {
ch := make(chan Result)
go runChain(ch)
res := <-ch
fmt.Println("result:", res.data)
}
多Goroutine协作的同步方案
当多个Goroutine都做链式处理,并且需要等全部完成再继续,可以用带缓冲Channel或者WaitGroup配合Channel。
| 方案 | 适用场景 |
|---|---|
| 无缓冲Channel | 单任务结果回收 |
| 缓冲Channel | 固定数量Goroutine结果收集 |
| WaitGroup加Channel | 动态数量任务且需汇总 |
常见错误
- 在Goroutine里直接读外部变量却不加同步,造成数据竞争
- 忘记从Channel接收,导致Goroutine永久阻塞
- 把链式调用结果赋给外部变量却不通过Channel传递
记住:链式调用本身不带来并发,Goroutine才带来并发,Channel负责让并发之间安全对话。
小结
链式调用放进Goroutine后只是普通的方法顺序执行被异步化。真正麻烦的是多Goroutine数据共享和结束通知,用Channel把结果传出来、用接收动作等Goroutine结束,就能写出清晰可靠的并发代码。