Goroutines, channels, and sync patterns for concurrent programming. Use when implementing concurrent operations.
This skill inherits all available tools. When active, it can use any tool Claude has access to.
Go concurrency patterns using goroutines, channels, and synchronization primitives.
Use this skill when:
// Start a goroutine
go func() {
fmt.Println("Hello from goroutine")
}()
// With parameters
go processItem(item)
// Anonymous function with closure
for _, item := range items {
item := item // capture range variable
go func() {
process(item)
}()
}
func worker(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
default:
doWork()
}
}
}
ctx, cancel := context.WithCancel(context.Background())
go worker(ctx)
// Later...
cancel() // stop the worker
// Unbuffered channel
ch := make(chan int)
// Buffered channel
ch := make(chan int, 10)
// Send
ch <- 42
// Receive
value := <-ch
// Receive with ok
value, ok := <-ch
if !ok {
// channel closed
}
// Close
close(ch)
func fanOut(input <-chan int, workers int) []<-chan int {
channels := make([]<-chan int, workers)
for i := 0; i < workers; i++ {
channels[i] = worker(input)
}
return channels
}
func fanIn(channels ...<-chan int) <-chan int {
out := make(chan int)
var wg sync.WaitGroup
for _, ch := range channels {
wg.Add(1)
go func(c <-chan int) {
defer wg.Done()
for v := range c {
out <- v
}
}(ch)
}
go func() {
wg.Wait()
close(out)
}()
return out
}
func generator(nums ...int) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for _, n := range nums {
out <- n
}
}()
return out
}
func square(in <-chan int) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for n := range in {
out <- n * n
}
}()
return out
}
// Usage
nums := generator(1, 2, 3, 4)
squares := square(nums)
for v := range squares {
fmt.Println(v)
}
func workerPool(ctx context.Context, jobs <-chan Job, results chan<- Result, numWorkers int) {
var wg sync.WaitGroup
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for {
select {
case <-ctx.Done():
return
case job, ok := <-jobs:
if !ok {
return
}
result := processJob(job)
select {
case results <- result:
case <-ctx.Done():
return
}
}
}
}()
}
wg.Wait()
close(results)
}
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
process(id)
}(i)
}
wg.Wait() // wait for all goroutines
type SafeCounter struct {
mu sync.Mutex
count int
}
func (c *SafeCounter) Inc() {
c.mu.Lock()
defer c.mu.Unlock()
c.count++
}
func (c *SafeCounter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.count
}
type Cache struct {
mu sync.RWMutex
items map[string]*Item
}
func (c *Cache) Get(key string) *Item {
c.mu.RLock()
defer c.mu.RUnlock()
return c.items[key]
}
func (c *Cache) Set(key string, item *Item) {
c.mu.Lock()
defer c.mu.Unlock()
c.items[key] = item
}
var (
instance *Singleton
once sync.Once
)
func GetInstance() *Singleton {
once.Do(func() {
instance = &Singleton{}
})
return instance
}
import "golang.org/x/sync/errgroup"
func processParallel(ctx context.Context, items []Item) error {
g, ctx := errgroup.WithContext(ctx)
for _, item := range items {
item := item // capture
g.Go(func() error {
return processItem(ctx, item)
})
}
return g.Wait() // returns first error
}
func handleRequests(ctx context.Context, requests <-chan Request, timeout time.Duration) {
for {
select {
case <-ctx.Done():
return
case req := <-requests:
processRequest(req)
case <-time.After(timeout):
log.Println("timeout")
}
}
}
// Good - can be stopped
func worker(ctx context.Context) {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return // exit path
case <-ticker.C:
doWork()
}
}
}
// Bad - goroutine leak
func worker() {
ticker := time.NewTicker(time.Second)
for range ticker.C {
doWork() // no way to stop
}
}
func generator(done <-chan struct{}) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for i := 0; ; i++ {
select {
case out <- i:
case <-done:
return
}
}
}()
return out
}
value, ok := <-ch// Bad
for _, item := range items {
go func() {
process(item) // wrong item!
}()
}
// Good
for _, item := range items {
item := item // capture
go func() {
process(item)
}()
}
// Bad - leaks goroutine
func leak() {
ch := make(chan int)
go func() {
ch <- 1 // blocks forever if no receiver
}()
}
// Good - can complete
func noLeak() {
ch := make(chan int, 1) // buffered
go func() {
ch <- 1 // won't block
}()
}
// Bad - data race
var counter int
for i := 0; i < 10; i++ {
go func() {
counter++ // race!
}()
}
// Good - synchronized
var mu sync.Mutex
var counter int
for i := 0; i < 10; i++ {
go func() {
mu.Lock()
counter++
mu.Unlock()
}()
}