直接跑一下看看呢
package main import ( "context" "fmt" "strconv" "sync" "time" ) func main() { timeCtx, cancle := context.WithTimeout(context.Background(), time.Second*10) defer cancle() Test(timeCtx) } func Test(ctx context.Context) { finish := make(chan bool) list := []string{} for i := 0; i < 100000; i++ { list = append(list, strconv.Itoa(i)) } taskList := make(chan string, len(list)) // 为啥要close管道-因为不close下面的range是不会出来的 defer close(taskList) for _, v := range list { taskList <- v } timeOut := make(chan bool) go func() { var wg sync.WaitGroup wg.Add(len(list)) // 限频3个消费者 for i := 0; i < 100; i++ { // 起3个协程去跑 go func() { for v := range taskList { select { case <-timeOut: func() { defer wg.Done() }() default: func() { defer wg.Done() ajax(v) }() } } }() } wg.Wait() fmt.Println(51) // 全部执行完成 finish<-true }() fmt.Println(60) select { case <-ctx.Done(): close(timeOut) // 超时 case <-finish: // 执行完成 fmt.Println("222") close(timeOut) } // done } func ajax(v string) { time.Sleep(time.Second * 1) fmt.Println(v) }
以上用于组装api接口请求
在最顶层的 timeout 抛出以后,下层会立即返回结果,并且终止待请求任务的发送;
通过消费者数量限定并行请求的数量 ;
实在没能想到其他更简单一些的方式、以及代码更简单清晰的方式 ~
context对象应用于多个层级的子协程,我这边的是一个按照配置的走的无限级递归调用的方式~