Skip to content

Concurrency EN

sungaowei edited this page Oct 16, 2026 · 1 revision

Concurrency

English | 简体中文


A Client is safe for concurrent use by any number of goroutines. It holds no per-request state, its configuration is read-only after construction, and the underlying http.Client pools connections (MaxIdleConnsPerHost: 100).

Create one client, share it, and pass a context.Context per call for cancellation and deadlines:

var wg sync.WaitGroup
for _, ticket := range tickets {
	wg.Add(1)
	go func(ticket string) {
		defer wg.Done()
		response, err := client.SystemOne(ctx, ticket, questions)
		if err != nil {
			log.Printf("%s: %v", ticket, err)
			return
		}
		fmt.Printf("%s -> %s\n", ticket, response.Choices()["category"].Choice)
	}(ticket)
}
wg.Wait()

Bounding Concurrency

Unbounded goroutines mean unbounded in-flight requests. Use a semaphore or a worker pool:

Semaphore

sem := make(chan struct{}, 16) // at most 16 calls in flight
var wg sync.WaitGroup

for _, ticket := range tickets {
	wg.Add(1)
	go func(ticket string) {
		defer wg.Done()
		sem <- struct{}{}
		defer func() { <-sem }()

		response, err := client.SystemOne(ctx, ticket, questions)
		// ...
	}(ticket)
}
wg.Wait()

Worker Pool

jobs := make(chan Ticket)
var wg sync.WaitGroup

for range runtime.NumCPU() {
	wg.Add(1)
	go func() {
		defer wg.Done()
		for ticket := range jobs {
			// one SystemOne call per ticket
		}
	}()
}

Cancellation

Every call takes a context, so cancelling it aborts the request immediately:

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

response, err := client.SystemOne(ctx, state, questions)

A cancelled or expired context is never retried: the SDK returns the failure immediately instead of extending a call the caller has already given up on. See Retry Policy.


Closing

Close releases idle connections. Call it once when the application shuts down; it is idempotent, and subsequent calls return the first call's error.

Clone this wiki locally