在稍复杂一些的并发程序中,需要考虑通过 CSP 模型输入/输出原语的承载体 channel 在 goroutine 之间建立联系。
为了满足这一需求,通常使用下面的方式来创建一个 goroutine:
type T struct{ ... }
func spawn(f func()) chan T {
c := make(chan T)
go func() {
// 使用channel变量c(通过闭包方式)与调用spawn的goroutine通信
... ...
f()
... ...
}()
return c
}
func main() {
c := spawn(func() {})
// 使用channel变量c与新创建的goroutine通信
}
这个在内部创建一个 goroutine 并返回一个 channel 类型变量的函数就是 Go 中最常见的 goroutine 创建模式。
spawn 函数创建的新 goroutine 与调用 spawn 函数的 goroutine 之间通过一个 channel 建立起了联系:两个 goroutine 可以通过这个 channel 进行通信。
e.g.
生成一个数据流:
func spawn(start, count int) <-chan int {
c := make(chan int)
go func() {
defer close(c)
for i := start; i < start+count; i++ {
c <- i
}
}()
return c
}
e.g.
获取数据流中的前 n 个数据:
func spawn(ctx context.Context, values ...any) <-chan any {
c := make(chan any)
go func() {
defer close(c)
for _, v := range values {
select {
case <-ctx.Done():
return
case c <- v:
}
}
}()
return c
}
func takeN(ctx context.Context, in <-chan any, num int) <-chan any {
c := make(chan any)
go func() {
defer close(c)
for i := 0; i < num; i++ {
select {
case <-ctx.Done():
return
case v, ok := <-in: // 从输入流中读取元素
if !ok {
return
}
c <- v
}
}
}()
return c
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// 创建流
in := spawn(ctx, []any{1, 2, 3, 4, 5}...)
// 只取流中的前n个数据
out := takeN(ctx, in, 3)
for v := range out {
fmt.Println(v)
}
}
// Output:
// 1
// 2
// 3