2

What I am trying to do? I am new to this

I have an array like this

  input := []int{20, 2112, 212, 12, 312, 231, 321312, 22, 31, 321, 4, 123, 2, 231213, 4, 23, 312, 312, 312321}

I am trying get the min and max value using concurrency in go lang.

Method I thought of

  1. I sliced the input array above at mid point input[:len(input)/2] input[len(input)/2:]. So I have two arrays now and I want to calculate the min and max of each and pass it to channels below. Min value goes to mins channel and max value goes to max channel
  2. Created two channel for storing minimum values(mins) and maximum values(maxs)
  3. Created two go routines for each sliced array which will calculate the min and max for each.

Problem

  1. Why I am getting a deadlock ? I am not not able to close it either

My attempt

package main

import "fmt"

func main() {
    Minmax_Method([]int{20, 2112, 212, 12, 312, 231, 321312, 22, 31, 321, 4, 123, 2, 231213, 4, 23, 312, 312, 312321})
}

func Minmax_Method(input []int) {

    mins := make(chan int) // channel for mins
    maxs := make(chan int) // channel for maxs

    go CalculateMinMaxWithChannel(input[:len(input)/2], mins, maxs)
    go CalculateMinMaxWithChannel(input[len(input)/2:], mins, maxs)

    // close(mins)
    // close(maxs)

    for i := range mins {
        fmt.Println("Minimums", i)
    }

    for j := range maxs {
        fmt.Println("Maximums", j)
    }

}

func CalculateMinMaxWithChannel(input []int, mins chan int, maxs chan int) {

    var min int = input[0]
    var max int = input[1]

    if min > max {
        min = input[1]
        max = input[0]
    }

    for i := 0; i < len(input); i++ {
        if input[i] < min {
            min = input[i]
        }
        if input[i] > max {
            max = input[i]
        }
    }

    mins <- min
    maxs <- max
}
  • 1
    You are only reading from `mins`, you are not exiting this loop ever while you go routines are deadlock on writing to the `maxs` channel. Try to read from both channels using `select` https://go.dev/tour/concurrency/5). Also determine you end state, i.e. when to exit the loop. – Eelco Oct 10 '22 at 16:56
  • @Eelco Thank you, I am going through the concepts again. by end state you mean `close()` or select case? – Latheesh V M Villa Oct 10 '22 at 17:23
  • 1
    By end state I mean the moment that you end the while loop. You could, for example, end the loop when one of the channels is closed. Or you could define a channel that only exists to signal that the loop can be exited. – Eelco Oct 11 '22 at 14:10

3 Answers3

0

Here is the revised Minmax_Method() function, it is not the best solution but for now it will be the solution for deadlock.

func Minmax_Method(input []int) {

    var minC int = 0
    var maxC int = 0

    mins := make(chan int) // channel for mins
    maxs := make(chan int) // channel for maxs

    defer close(mins)
    defer close(maxs)

    go CalculateMinMaxWithChannel(input[:len(input)/2], mins, maxs)
    go CalculateMinMaxWithChannel(input[len(input)/2:], mins, maxs)

    go func() {
        for i := range mins {
            minC++
            fmt.Println("Minimums", i)
            if minC == 2 && maxC == 2 {
                os.Exit(0)
            }
        }
    }()

    go func() {
        for j := range maxs {
            maxC++
            fmt.Println("Maximums", j)
            if minC == 2 && maxC == 2 {
                os.Exit(0)
            }
        }
    }()

    for {
    }
}
shikhar.code
  • 41
  • 1
  • 8
  • 1
    This solution has [data races](https://go.dev/doc/articles/race_detector) (`minC`/`maxC`) and the use of `os.Exit` really limits where it could be used. Using a `select` (as suggested by @Eelco) would avoid this and make it simpler to exit cleanly (something like [this](https://go.dev/play/p/445ejYEHaUW)). – Brits Oct 11 '22 at 00:59
0

Brits's code solves this problem well, here is more simplefy code

package main

import (
    "fmt"
    "sync"
)

func main() {
    Minmax_Method([]int{20, 2112, 212, 12, 312, 231, 321312, 22, 31, 321, 4, 123, 2, 231213, 4, 23, 312, 312, 312321})
}

func CalculateMinMaxWithChannel(input []int, mins chan int, maxs chan int) {

    var min = input[0]
    var max = input[0]

    for i := 0; i < len(input); i++ {
        if input[i] < min {
            min = input[i]
        }
        if input[i] > max {
            max = input[i]
        }
    }

    mins <- min
    maxs <- max
}

func Minmax_Method(input []int) {
    mins := make(chan int) // channel for mins
    maxs := make(chan int) // channel for maxs

    var wg sync.WaitGroup
    wg.Add(2)

    go func() {
        CalculateMinMaxWithChannel(input[:len(input)/2], mins, maxs)
        wg.Done()
    }()

    go func() {
        CalculateMinMaxWithChannel(input[len(input)/2:], mins, maxs)
        wg.Done()
    }()

    go func() {
        wg.Wait()
        close(mins)
        close(maxs)
    }()

    var minC = input[0]
    var maxC = input[0]

    for mins != nil && maxs != nil {
        select {
        case m, ok := <-mins:
            if !ok {
                mins = nil
                continue
            }
            if m < minC {
                minC = m
            }
        case m, ok := <-maxs:
            if !ok {
                maxs = nil
                continue
            }
            if m > maxC {
                maxC = m
            }
        }
    }

    fmt.Println("Maximum:", maxC, "Minimum:", minC)
}
hongfei
  • 26
  • 2
0

As we have only two arrays to iterate. Based on performance test this method is not much effective (not sure why). It's better to solve it using the normal for loop or divide and conquer method. Have not tested parallelism

package main

import "fmt"

func main() {
    out := Minmax_Method([]int{20, 2112, 212, 12, 312, 231, 321312, 22, 31, 321, 4, 123, 2, 231213, 4, 23, 312, 312, 312321})
    fmt.Println("Minimum value is: ", out.Min)
    fmt.Println("Maximum value is: ", out.Max)
}

type Output struct {
    Min int
    Max int
}

func Minmax_Method(input []int) Output {

    mins := make(chan int) // channel for mins
    maxs := make(chan int) // channel for maxs

    go CalculateMinMaxWithChannel(input[:len(input)/2], mins, maxs)
    go CalculateMinMaxWithChannel(input[len(input)/2:], mins, maxs)

    min1, min2, max1, max2 := <-mins, <-mins, <-maxs, <-maxs //take value from channel

    var minVal int
    var maxVal int
    if min1 <= min2 {
        minVal = min1
    } else {
        minVal = min2
    }

    if max1 >= max2 {
        maxVal = max1
    } else {
        maxVal = max2
    }

    return Output{Min: minVal, Max: maxVal}
}

func CalculateMinMaxWithChannel(input []int, mins chan int, maxs chan int) {

    var min int = input[0]
    var max int = input[1]

    if min > max {
        min = input[1]
        max = input[0]
    }

    for i := 0; i < len(input); i++ {
        if input[i] < min {
            min = input[i]
        }
        if input[i] > max {
            max = input[i]
        }
    }

    mins <- min // add mins to mins channel
    maxs <- max // add maxs to max channel
}