Heim >Backend-Entwicklung >Golang >Erstellen eines robusten Aufgabenausführungskontexts in Go

Erstellen eines robusten Aufgabenausführungskontexts in Go

Susan Sarandon
Susan SarandonOriginal
2025-01-01 01:02:10280Durchsuche

Building a Robust Task Execution Context 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 ->

Das Problem

Berücksichtigen Sie diese häufigen Herausforderungen bei der Bewältigung gleichzeitiger Aufgaben:

  1. Sammeln von Fehlern aus mehreren Goroutinen
  2. Aufrechterhaltung der Thread-Sicherheit
  3. Begrenzung gleichzeitiger Ausführungen
  4. Der erste Fehler bleibt erhalten, während alle Fehler erfasst werden
  5. Saubere Fehlerbehandlungsmuster

Die Lösung: TaskContext

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}
}

Hauptmerkmale

1. Thread-sichere Fehlerbehandlung

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
}

2. Einzelaufgabenausführung

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
}

3. Parallele Aufgabenausführung

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()
}

4. Kontrollierte Parallelität

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 ...
}

Anwendungsbeispiele

Einfache Aufgabenausführung

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}
}

Parallele Aufgabenausführung

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
}

Vorteile

  1. Thread-Sicherheit: Alle Vorgänge sind durch Mutexe geschützt
  2. Fehlersammlung: Behält sowohl den ersten Fehler als auch alle Fehler bei
  3. Kontextintegration: Funktioniert mit dem Kontextpaket von Go
  4. Generischer Support: Funktioniert mit jedem Rückgabetyp
  5. Parallelitätskontrolle: Integrierte Unterstützung zur Begrenzung paralleler Ausführungen

Testen

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")
    })
}

Abschluss

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:

  • Mehrere Aufgaben gleichzeitig ausführen
  • Fehler aller Aufgaben sammeln
  • Gleichzeitige Ausführungen begrenzen
  • Thread-Sicherheit beibehalten
  • Behalten Sie den ersten Fehler im Auge, während Sie alle Fehler sammeln

Der vollständige Code ist auf GitHub verfügbar.

Ressourcen

  • Go-Kontextpaket
  • Go Parallelitätsmuster
  • Fehlerbehandlung in Go

Welche Muster verwenden Sie für die gleichzeitige Aufgabenausführung in Go? Teilen Sie Ihre Gedanken in den Kommentaren unten mit!

  • https://x.com/mahadev_k_
  • https://in.linkedin.com/in/mahadev-k-934520223

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!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn