Design and implement safe async/await and concurrency in application code: cancellation, timeouts, structured task lifetimes, shared-state races, and shutdown. Use when async, await, concurrency, 并发, race condition in code, CancellationToken/Context, Promise.all, goroutines, tokio tasks, or concurrent workers. Not for HTTP TOCTOU / business limit-overrun testing (see race-condition).
Instrucciones de origen · Vista previa de solo lectura
name
async-concurrency-patterns
description
Design and implement safe async/await and concurrency in application code: cancellation, timeouts, structured task lifetimes, shared-state races, and shutdown. Use when async, await, concurrency, 并发, race condition in code, CancellationToken/Context, Promise.all, goroutines, tokio tasks, or concurrent workers. Not for HTTP TOCTOU / business limit-overrun testing (see race-condition).
Async And Concurrency Patterns
Engineering design for in-process concurrency: who owns work, how it stops,
how shared state stays correct, and how failures surface. Language-agnostic
principles with concrete patterns for common runtimes. Prefer the repo’s
existing async style over inventing a second model.
Use When
Designing or fixing async/await, futures, coroutines, or callback chains
Cancellation, timeouts, cooperative abort, or request-scoped lifetime
Structured concurrency: parent tasks own children; no orphaned fire-and-forget
In-memory / multi-task races: double-init, stale reads, lost updates, shutdown races
Worker pools, fan-out/fan-in, pipelines, backpressure, and graceful shutdown
User mentions: async, concurrency, 并发, race condition (in code), deadlock,
CancellationToken, asyncio.TaskGroup, Promise.allSettled, goroutine leak
General reliability, errors, tests, security hygiene
code-quality-standards
Language formatting / naming only
matching *-style-* skill
Distributed locking / multi-node consensus deep design
domain + ops docs; this skill stays process-local
Note: HTTP race vulnerability testing is race-condition. Application
concurrency design (this skill) still applies when implementing atomic
fixes, idempotency, or transactional check-then-act in code.
Repo Config First
Repo config and neighboring async code outrank this skill’s defaults.
Runtime model: single-threaded event loop (JS, asyncio default), M:N
green threads (Go, Tokio), OS threads (Java, .NET ThreadPool), actor systems
Neighboring code: copy 2–3 mature services’ patterns for spawn, cancel,
and error aggregation before inventing new helpers
Precedence: If repo rules conflict with defaults below, follow the repo.
Surface conflicts that leave orphaned tasks, swallowed cancellation, or
unbounded fan-out.
// Go: errgroup cancels siblings on first error
g, ctx := errgroup.WithContext(ctx)
g.SetLimit(8)
for _, id := range ids {
id := id
g.Go(func()error { return fetchItem(ctx, id) })
}
return g.Wait()
Bad — orphaned work, no cancel, unbounded spawn:
// Bad: detached promises; errors become unhandled rejectionsfor (const id of ids) {
fetchItem(id); // no await, no signal, no limit
}
// Bad: leaked goroutines; no contextfor _, id := range ids {
go fetchItem(context.Background(), id)
}
let seq = 0;
asyncfunctionsearch(q: string, signal: AbortSignal) {
const my = ++seq;
const res = await api.search(q, { signal });
if (my !== seq) return; // supersededrender(res);
}
Bad — last write wins even if older request finishes last:
asyncfunctionsearch(q: string) {
const res = await api.search(q); // no abort, no generationrender(res); // stale query can overwrite newer results
}
Shared mutable state
Good — serialize mutations or use atomic update:
# Bad pattern avoided: check-then-act without lockasyncwithself._lock:
ifself._balance >= amount:
self._balance -= amount
returnTruereturnFalse
Bad
# Race: two coroutines both pass the checkifself._balance >= amount:
await asyncio.sleep(0) # yield — windowself._balance -= amount
Shutdown
Good — stop intake, cancel or drain with timeout, then close resources.
Bad — process exit while tasks still write to closing connections; or
while True: await work() with no cancel/stop channel.
Anti-Patterns
Fire-and-forget without supervision, join, or error metrics
Swallowing cancellation (except: pass, empty .catch(() => {})) so cleanup never runs
Creating a new root timeout that extends past the caller’s deadline
Unbounded Promise.all / goroutine spawn on user-controlled batch size
Holding locks/mutexes across network I/O without a deliberate design
Using “sleep and hope” instead of events/conditions for coordination
Treating HTTP race exploit methodology as a substitute for application locking
(use race-condition for authorized vuln tests; fix with atomic design here)
Thread-unsafe lazy singletons and static mutable caches