Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Undetected "deadlock" while reading from channel

How do I deal with a situation where undetected deadlock occurs when reading results of execution of uncertain number tasks from a channel in a complex program, e.g. web server?

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    rand.Seed(time.Now().UTC().UnixNano())

    results := make(chan int, 100)

    // we can't know how many tasks there will be
    for i := 0; i < rand.Intn(1<<8)+1<<8; i++ {
        go func(i int) {
            time.Sleep(time.Second)
            results <- i
        }(i)
    }

    // can't close channel here 
    // because it is still written in
    //close(results)

    // something else is going on other threads (think web server)
    // therefore a deadlock won't be detected
    go func() {
        for {
            time.Sleep(time.Second)
        }
    }()

    for j := range results {
        fmt.Println(j)
        // we just stuck in here
    }
}

In case of simpler programs go detects a deadlock and properly fails. Most examples either fetch a known number of results, or write to the channel sequentially.

like image 566
sanmai Avatar asked Mar 12 '23 17:03

sanmai


1 Answers

The trick is to use sync.WaitGroup and wait for the tasks to finish in a non-blocking way.

var wg sync.WaitGroup

// we can't know how many tasks there will be
for i := 0; i < rand.Intn(1<<8)+1<<8; i++ {
    wg.Add(1)
    go func(i int) {
        time.Sleep(time.Second)
        results <- i
        wg.Done()
    }(i)
}

// wait for all tasks to finish in other thread
go func() {
    wg.Wait()
    close(results)
}()

// execution continues here so you can print results

See also: Go Concurrency Patterns: Pipelines and cancellation - The Go Blog

like image 114
sanmai Avatar answered Mar 19 '23 19:03

sanmai