func ListDirectory(dir string) ([]string, error)
func ListDirectory(dir string) chan string
func ListDirectory(dir string, fn func(string))
func leak() {
ch := make(chan int)
go func() {
val := <- ch
fmt.Println("We received a value:", val)
}()
}
因此启动goroutine必须想清楚两个问题:
下面的两个例子,都是体现出这两个问题的方案。
有两个http后台服务,要求一个结束时,另一个也要结束,采用如下方法:
package main
import (
"context"
"fmt"
"net/http"
)
func Serve(addr string, handler http.Handler, stop <-chan struct{}) error {
server := http.Server{
Addr: addr,
Handler: handler,
}
go func() {
<- stop
server.Shutdown(context.Background())
}()
return server.ListenAndServe()
}
type MyStruct struct {
}
func (m *MyStruct) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
path := request.URL.Path
writer.Write([]byte(fmt.Sprintf("Welcome %s", path)))
}
func main() {
stop := make(chan struct{}) // 是否要分配容量?
done := make(chan error, 2) // 是否要分配容量?
handler := new(MyStruct)
// 开启两个服务
go func() {
done <- Serve(":8012", handler, stop)
}()
go func() {
done <- Serve(":9000", handler, stop)
}()
var stopped bool
for i := 0; i < cap(done); i++ {
if err := <- done; err != nil {
fmt.Printf("error: %v\n", err)
}
if !stopped {
stopped = true
close(stop) // close只做一次
}
}
fmt.Println("main goroutine end")
}
一个埋点服务,假设每个http请求过来时,都需要做埋点的Event上报处理。
package main
import (
"context"
"fmt"
"time"
)
type Tracker struct {
ch chan string
stop chan struct{}
}
func NewTracker() *Tracker {
return &Tracker{
ch: make(chan string, 10), // 容量10, 作为一个buffer使用
}
}
// Event 把原来真正的任务操作,简化为放入buf中,而任务操作由后台固定数量线程执行
func (t *Tracker) Event(ctx context.Context, data string) error {
select {
case t.ch <- data:
return nil
case <-ctx.Done():
return ctx.Err()
}
}
// Run 后台线程 从buf中取数据进行执行,使用for的写法,则只允许启动一个goroutine执行Run函数
func (t *Tracker) Run() {
for data := range t.ch {
// 这里可以写成多个goroutine
time.Sleep(10 * time.Second)
fmt.Println(data)
}
t.stop <- struct{}{}
}
func (t *Tracker) Shutdown(ctx context.Context) error {
close(t.ch) // 关闭ch,则不能再往里写
select {
case <-t.stop:
return nil
case <-ctx.Done():
return ctx.Err()
}
}
// 一个埋点服务
func main() {
tr := NewTracker()
go tr.Run() // 后台线程不断消耗buf数据
_ = tr.Event(context.Background(), "test1") // 往chan中写
_ = tr.Event(context.Background(), "test2")
_ = tr.Event(context.Background(), "test3")
time.Sleep(3 * time.Second)
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second))
defer cancel()
tr.Shutdown(ctx)
}