Heim >Backend-Entwicklung >Golang >Erstellen eines robusten Aufgabenausführungskontexts in Go
Dies könnte meine letzte Sicht auf die Fehlerbehandlung in Go sein. Ich denke, das ist auch das Beste. Wir wissen, dass jede Anweisung, die wir ausführen, in einem Kontext steht. Und der Kontext kann Fehler enthalten. Da überlegte ich, warum ich nicht einfach einen Wrapper über dem aktuellen Kontext erstellen sollte. Wenn also die gesamte Aufgabe über einen bestimmten FN ausgeführt wird, können wir möglicherweise prüfen, ob der CTX einen Fehler aufweist, und wenn ja, nicht ausführen, andernfalls ausführen und den Fehler erfassen. Das könnte sich zu einem Anti-Muster entwickeln, aber ja, bis es dazu kommt, können wir versuchen, herumzuspielen.
Nun, Cursor hatte ein paar Dinge hinzuzufügen ->
Berücksichtigen Sie diese häufigen Herausforderungen bei der Bewältigung gleichzeitiger Aufgaben:
Lassen Sie uns einen TaskContext erstellen, der diese Probleme löst:
package taskctx import ( "context" "errors" "fmt" "sync" ) type RunFn[T any] func() (T, error) type TaskContext struct { context.Context mu sync.RWMutex err error multiErr []error } func NewTaskContext(parent context.Context) *TaskContext { if parent == nil { panic("cannot create context from nil parent") } return &TaskContext{Context: parent} }
func (c *TaskContext) WithError(err error) *TaskContext { if err == nil { return c } c.mu.Lock() defer c.mu.Unlock() c.multiErr = append(c.multiErr, err) if c.err == nil { c.err = err } else { c.err = errors.Join(c.err, err) } return c }
func Run[T any](ctx *TaskContext, fn RunFn[T]) T { var zero T if err := ctx.Err(); err != nil { return zero } result, err := fn() if err != nil { ctx.WithError(err) return zero } return result }
func RunParallel[T any](ctx *TaskContext, fns ...func() (T, error)) ([]T, error) { if err := ctx.Err(); err != nil { return nil, err } results := make([]T, len(fns)) var resultsMu sync.Mutex var wg sync.WaitGroup wg.Add(len(fns)) for i, fn := range fns { i, fn := i, fn go func() { defer wg.Done() result, err := fn() if err != nil { ctx.AddError(fmt.Errorf("task %d: %w", i+1, err)) } else { resultsMu.Lock() results[i] = result resultsMu.Unlock() } }() } wg.Wait() return results, ctx.Errors() }
func RunParallelWithLimit[T any](ctx *TaskContext, limit int, fns ...func() (T, error)) ([]T, error) { // ... similar to RunParallel but with semaphore ... sem := make(chan struct{}, limit) // ... implementation ... }
func ExampleTaskContext_ShipmentProcessing() { ctx := goctx.NewTaskContext(context.Background()) order := dummyOrder() shipment := dummyShipment() // Step 1: Validate address // Step 2: Calculate shipping cost // Step 3: Generate label _ = goctx.Run(ctx, validateAddress("123 Main St")) cost := goctx.Run(ctx, calculateShipping(order)) trackingNum := goctx.Run(ctx, generateLabel(shipment.OrderID, cost)) if ctx.Err() != nil { fmt.Printf("Error: %v\n", ctx.Err()) return } shipment.Status = "READY" shipment.TrackingNum = trackingNum fmt.Printf("Shipment processed: %+v\n", shipment) // Output: // Shipment processed: {OrderID:ORD123 Status:READY TrackingNum:TRACK-ORD123-1234567890} }
func ExampleTaskContext_OrderProcessing() { ctx := goctx.NewTaskContext(context.Background()) // Mock order order := []OrderItem{ {ProductID: "LAPTOP", Quantity: 2}, {ProductID: "MOUSE", Quantity: 3}, } taskCtx := goctx.NewTaskContext(ctx) // Create inventory checks for each item inventoryChecks := goctx.Run[[]goctx.RunFn[bool]](taskCtx, func() ([]goctx.RunFn[bool], error) { return streams.NewTransformer[OrderItem, goctx.RunFn[bool]](order). Transform(streams.MapItSimple(checkInventory)). Result() }) // Run inventory checks in parallel _, err := goctx.RunParallel(ctx, inventoryChecks...) fmt.Printf("Inventory check error: %v\n", err) // Output: // Inventory check error: task 1: insufficient inventory for LAPTOP }
So testen Sie die Implementierung:
func TestTaskContext(t *testing.T) { t.Run("handles parallel errors", func(t *testing.T) { ctx := NewTaskContext(context.Background()) _, err := RunParallel(ctx, func() (int, error) { return 0, errors.New("error 1") }, func() (int, error) { return 0, errors.New("error 2") }, ) assert.Error(t, err) assert.Contains(t, err.Error(), "error 1") assert.Contains(t, err.Error(), "error 2") }) }
Diese TaskContext-Implementierung bietet eine robuste Lösung für die gleichzeitige Ausführung von Aufgaben mit ordnungsgemäßer Fehlerbehandlung in Go. Dies ist besonders nützlich, wenn Sie Folgendes benötigen:
Der vollständige Code ist auf GitHub verfügbar.
Welche Muster verwenden Sie für die gleichzeitige Aufgabenausführung in Go? Teilen Sie Ihre Gedanken in den Kommentaren unten mit!
Das obige ist der detaillierte Inhalt vonErstellen eines robusten Aufgabenausführungskontexts in Go. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!