ワンクリックで
go-concurrency
Go Concurrency Skill
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Go Concurrency Skill
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
| name | go-concurrency |
| description | Go Concurrency Skill |
Version: 1.0 Purpose: Idiomatic patterns for goroutines, channels, and context in Go Source: Adapted from Effective Go, Go by Example, and production patterns
Go provides built-in concurrency primitives via goroutines and channels. Unlike Effect's fibers, Go uses goroutines (actual OS threads in the Go scheduler) for concurrency.
| Aspect | OS Threads | Go Goroutines |
|---|---|---|
| Memory | ~8MB stack each | ~2KB initial stack (grows as needed) |
| Limit | Hundreds (system limit) | Hundreds of thousands |
| Scheduling | OS kernel (preemptive) | Go runtime (cooperative + preemptive) |
| Best for | CPU-bound parallelism | Mixed I/O and CPU work |
Key insight: Goroutines are lightweight and scale well for I/O-bound work. Use them liberally.
// Spawn goroutine
go func() {
fmt.Println("running in background")
}()
// Main continues immediately
fmt.Println("main continues")
var wg sync.WaitGroup
// Spawn 3 workers
for i := 0; i < 3; i++ {
wg.Add(1) // Increment counter
go func(id int) {
defer wg.Done() // Decrement when done
doWork(id)
}(i)
}
// Wait for all to complete
wg.Wait()
// Channel with 10-item buffer
ch := make(chan int, 10)
// Non-blocking send
select {
case ch <- value:
fmt.Println("sent")
default:
fmt.Println("channel full")
}
// Blocking channel (size 0)
ch := make(chan int)
// Send blocks until receiver ready
ch <- value
// Receive blocks until sender ready
value := <-ch
CRITICAL: Always pass context.Context as first argument to functions that:
// Background context (no deadline, no cancellation)
ctx := context.Background()
// TODO context (when you don't know what to use yet)
ctx := context.TODO()
// With timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() // Always call to release resources
// With deadline
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second))
defer cancel()
// With cancellation value
ctx, cancel := context.WithCancel(parentCtx)
// Cancel later: cancel()
func doWork(ctx context.Context) error {
ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
// Context cancelled
return ctx.Err() // context.Canceled or context.DeadlineExceeded
case <-ticker.C:
// Do work
if err := process(); err != nil {
return err
}
}
}
}
// Parent context controls child
func (s *Service) Run(ctx context.Context) error {
// Child inherits parent's deadline/cancellation
childCtx, cancel := context.WithCancel(ctx)
defer cancel()
return s.doWork(childCtx)
}
// Merge multiple channels into one
func fanIn(ch1, ch2 <-chan int) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for {
select {
case v, ok := <-ch1:
if !ok { ch1 = nil } // Disable channel when closed
case v, ok := <-ch2:
if !ok { ch2 = nil }
default:
if ch1 == nil && ch2 == nil {
return // Both closed
}
}
if ch1 != nil || ch2 != nil {
out <- v
}
}
}()
return out
}
// Distribute work to multiple workers
func fanOut(work <-chan Task, workers int) {
for i := 0; i < workers; i++ {
go worker(work)
}
}
type WorkerPool struct {
tasks chan Task
results chan Result
wg sync.WaitGroup
}
func NewWorkerPool(workers int) *WorkerPool {
p := &WorkerPool{
tasks: make(chan Task, 100),
results: make(chan Result, 100),
}
p.wg.Add(workers)
for i := 0; i < workers; i++ {
go p.worker()
}
return p
}
func (p *WorkerPool) worker() {
defer p.wg.Done()
for task := range p.tasks {
result := process(task)
p.results <- result
}
}
func (p *WorkerPool) Submit(task Task) {
p.tasks <- task
}
func (p *WorkerPool) Close() []Result {
close(p.tasks)
p.wg.Wait()
close(p.results)
var results []Result
for r := range p.results {
results = append(results, r)
}
return results
}
// Send errors back from goroutine
errCh := make(chan error, 1)
go func() {
defer close(errCh)
errCh <- doWork()
}()
select {
case err := <-errCh:
if err != nil {
log.Printf("work failed: %v", err)
}
case <-time.After(5 * time.Second):
log.Printf("timeout waiting for work")
}
// Collect errors from multiple goroutines
g, ctx := errgroup.WithContext(context.Background())
g.Go(func() error {
return task1(ctx)
})
g.Go(func() error {
return task2(ctx)
})
// Wait for all - returns first error (if any)
if err := g.Wait(); err != nil {
log.Printf("one or more tasks failed: %v", err)
}
-racego test -race ./...
go run -race main.go
type Counter struct {
mu sync.Mutex
value int
}
func (c *Counter) Increment() {
c.mu.Lock()
defer c.mu.Unlock()
c.value++
}
type Counter struct {
value int64 // Must be int64 for atomic
}
func (c *Counter) Increment() {
atomic.AddInt64(&c.value, 1)
}
func (c *Counter) Get() int64 {
return atomic.LoadInt64(&c.value)
}
// ❌ WRONG: Receiver closes channel
func consumer(ch <-chan int) {
defer close(ch) // PANIC!
for v := range ch { ... }
}
// ✅ CORRECT: Sender owns and closes channel
func producer() <-chan int {
ch := make(chan int)
go func() {
defer close(ch) // Owner closes
for i := 0; i < 10; i++ {
ch <- i
}
}()
return ch
}
func asyncResult() <-chan int {
ch := make(chan int, 1) // Buffered so goroutine doesn't block
go func() {
defer close(ch)
ch <- longRunningCalculation()
}()
return ch
}
// Use later
result := <-asyncResult()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
select {
case result := <-ch:
fmt.Printf("got result: %v\n", result)
case <-ctx.Done():
fmt.Println("timeout")
}
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for range ticker.C {
doPeriodicWork()
}
// Limit to 10 requests per second
limiter := time.NewTicker(100 * time.Millisecond)
defer limiter.Stop()
for _, item := range items {
<-limiter.C // Wait for next token
process(item)
}
-race flag to detect race conditionsDiagnose Azedarach command latency and daemon/TUI performance with OpenTelemetry traces exported to a local Jaeger backend. Use when investigating slow `az` commands, TUI stalls, daemon RPC latency, missing spans, local Jaeger setup, or trace-driven performance regressions in this repository.
Review and integrate Azedarach issues that are in_review. Use when Codex is asked to run a reviewer/integrator session, sweep active issues that are ready for review, inspect worker evidence and diffs, return actionable findings to live or paused issue sessions, close accepted work, or get in_review issues integrated.
Review, consolidate, test, and harden Azedarach SQLite migrations before integration. Use for any migration, schema ensure/repair logic, trigger or index change, persistence-authority change, migration failure, or pre-merge review of a branch containing database changes. Also use when validating upgrades against real root-user or registered project databases.
Run an iterative code review and fix cycle until findings stabilize. Use when the user asks Codex to review code, review and fix, loop review/fix, address review findings, harden a change before handoff, or keep reviewing until repeated passes find no new actionable issues. Also use proactively before Codex declares coding work done, marks an issue in_review, says a change is ready, or hands off implementation that modified code, tests, build scripts, migrations, config, or developer tooling.
Go Testing Skill
Build and review production-grade Go logging and observability with `log/slog` and OpenTelemetry. Use when adding or refactoring logs, instrumenting request paths, defining event schemas, reducing log cost/cardinality, enforcing redaction/PII policy, correlating logs with traces, or reviewing Go services for observability gaps.