이것이 Go에서의 오류 처리에 대한 나의 마지막 견해일 것입니다. 저도 이게 제일 좋은 것 같아요. 우리는 실행하는 모든 명령이 컨텍스트에 있음을 알고 있습니다. 그리고 컨텍스트에 오류가 있을 수 있습니다. 이것은 단순히 현재 컨텍스트 위에 래퍼를 만드는 것이 어떨까 생각한 때입니다. 따라서 모든 작업이 특정 fn을 통해 실행되면 ctx에 오류가 있는지 확인할 수 있으며, 그렇다면 실행하지 말고 오류를 실행하고 수집하세요. 이는 안티 패턴이 될 수도 있지만 그렇게 될 때까지 우리는 놀아볼 수 있습니다.
글쎄, 커서에 추가할 것이 거의 없었습니다 ->
동시 작업을 처리할 때 다음과 같은 일반적인 문제를 고려하십시오.
이러한 문제를 해결하는 TaskContext를 구축해 보겠습니다.
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 }
구현을 테스트하는 방법은 다음과 같습니다.
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") }) }
이 TaskContext 구현은 Go에서 적절한 오류 처리를 통해 동시 작업 실행을 처리하기 위한 강력한 솔루션을 제공합니다. 다음과 같은 경우에 특히 유용합니다.
전체 코드는 GitHub에서 확인할 수 있습니다.
Go에서 동시 작업 실행을 처리하기 위해 어떤 패턴을 사용합니까? 아래 댓글로 여러분의 생각을 공유해주세요!
위 내용은 Go에서 강력한 작업 실행 컨텍스트 구축의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!