PPysolate SpineREAD THE CODE · UNDERSTAND THE SYSTEM
课程与章节
源码索引 / CURRENT IMPLEMENTATION

learning/go-patterns/stages/07-cancel-join/main.go

82 行 · 点击行号可定位;函数链接随声明位置更新,不手填行号。

跳到函数 / 声明
// Stage 07 keeps the concurrency limit and adds cancellation plus joining.
package main

import (
	"context"
	"errors"
	"fmt"
	"sync"
)

// Long-running tasks must observe ctx or pass it to a cancellable API.
type Task func(context.Context) (string, error)

// RunFailFast returns ordered results after every worker has stopped.
// Unstarted tasks retain empty results. The first worker to record an error
// cancels the group; simultaneous failures have no fixed winner.
func RunFailFast(parent context.Context, tasks []Task, limit int) ([]string, error) {
	if limit < 1 {
		return nil, errors.New("limit must be positive")
	}
	for i, task := range tasks {
		if task == nil {
			return nil, fmt.Errorf("task %d is nil", i)
		}
	}
	values := make([]string, len(tasks))
	ctx, cancel := context.WithCancel(parent)
	defer cancel()
	jobs := make(chan int)
	var workers sync.WaitGroup
	var first sync.Once
	var firstErr error
	for range min(limit, len(tasks)) {
		workers.Add(1)
		go func() {
			defer workers.Done()
			for index := range jobs {
				if ctx.Err() != nil {
					return
				}
				value, err := tasks[index](ctx)
				values[index] = value
				if err != nil {
					first.Do(func() { firstErr = err; cancel() })
					return
				}
			}
		}()
	}
dispatch:
	for index := range tasks {
		select {
		case jobs <- index:
		case <-ctx.Done():
			break dispatch
		}
	}
	close(jobs)
	workers.Wait()
	if firstErr != nil {
		return values, firstErr
	}
	return values, parent.Err()
}

func main() {
	started := make(chan struct{})
	stopped := make(chan struct{})
	wantErr := errors.New("shipping service unavailable")
	_, err := RunFailFast(context.Background(), []Task{
		func(ctx context.Context) (string, error) {
			close(started)
			defer close(stopped)
			<-ctx.Done()
			return "", ctx.Err()
		},
		func(context.Context) (string, error) { <-started; return "", wantErr },
	}, 2)
	<-stopped
	fmt.Println("error:", err)
	fmt.Println("workers stopped")
}