Monkeys, Juggling and some Golang

Today let’s do some basic monkey work, rather lets do some juggling. I am always fascinated by people being able to juggle multiple balls. It’s something amazing, at least to me. They even have a World Juggling day, can you believe it?! So, why are we talking about monkeys, juggling and what not… Well, to me golang goroutines is sort of juggling. Where the juggler is able to do one thing at one time and, is able to keep track of multiple balls or whatever is being juggled. Well, you might have a different opinion and you are right, if the opinion is something like we can truely do tasks parallely if there are multiple CPUs, akin to two monkeys handling 2 seperate balls. hmm… yeah, so this might be the most over simplified and in some ways incorrect expalnation of concurrency and parallelism. For people who are curious to know the difference, there are awesome videos out there, particularly one comes to mind, the one Rob Pike gave. Check it out, you might like it.

I initially thought of writing about different ways in which we can do concurrent jobs in both golang and python. Well, now that I am thinking let me give an example of how to use go routines.

Go routines are basically light threads handled by the go scheduler to do concurrent jobs. There are primitives such as mutexes, synchonozation etc. to prevent conditions like data lock, unwanted write, multiple threads trying to read and write a resource at the sametime etc. more on that later.

Magical number e

So let’s see how to find the value of e in three different ways. I think if you ask me what is your favorite number, I might say it is e, never ending, rather transcendental constant with value 2.7182818… . Funny thing is that this number comes up in many places across the universe, it was made famous by Bernoulli when he came across it, while studying compound interest..go figure out..>>

An example program to find value of e

There are lot of ways to find e, we can use the Binomial expansion, Newton’s method, Brother’s method (formualated in 2004 I think) etc.

package main

    import (
        "fmt"
        "math"
        "sync"
    )

    // dumbFact is a recursive function to print factorial
    func dumbFact(num float64) float64 {
        if num <= 1 {
            return 1
        }
        return num * dumbFact(num-1)
    }

    // Brother method to find e using approximation
    // for n=0 to limit
    //    (2n+2)/(2n+1)!
    func Brother(limit float64, wg *sync.WaitGroup) {
        var n float64
        var e float64
        // `defer` keyword will run the expression after the
        //function returns
        defer wg.Done()
        for n <= limit {
            e = e + (2*n+2)/dumbFact(2*n+1)
            n = n + 1
        }
        fmt.Printf("Brother's method for `e` yields: %20.15f\n", e)
    }

    // Binomial method to find `e`
    func Binomial(limit float64, wg *sync.WaitGroup) {
        defer wg.Done()
        e := math.Pow((1 + 1/limit), limit)
        fmt.Printf("Binomial solution to `e` is : %20.15f\n", e)
    }

    // Newton method - approximation when n is big
    func Newton(limit float64, wg *sync.WaitGroup) {
        defer wg.Done()
        var n float64
        var e float64
        for n <= limit {
            e = e + 1/dumbFact(n)
            n = n + 1
        }
        fmt.Printf("Newton's method gives the value as: %20.15f\n", e)
    }

    func main() {
        fmt.Println("A simple go routine example")
        // A waitgroup is just used to wait until the
        //routine completes
        var wg sync.WaitGroup
        wg.Add(3)
        go Brother(10000, &wg)
        go Binomial(10000, &wg)
        go Newton(10000, &wg)
        wg.Wait()
        fmt.Println("Done!")
    }

You can see the program in action here.

What’s happening here?

The juggling happens in lines 58, 59 and 60 of the program. Where, the scheduler will concurrently work on the three routines, there is nothing complicated in the program, it uses the basic factorial function, uses something called the WaitGroup and runs three go routines.

So why am I using a WaitGroup, you ask. Well, WaitGroup is just to prevent go from falling through and terminating the programming before the routines are done. It tells go to wait till all the routines have finished running. We could waited for a simple input from the user at the end after line 61 and made the program explicitly wait, which would have given the routines time to run as well. I arbitrarily chose this WaitGroup option.

The end

Essentially, go routines are simple at first, and that is important. As in any concurrent programming paradigms, there are complications and real world pains, but the entry level bar is low, which is in my opinion neat. This helps one learn the concepts of concurrency eventhough you might not use Go. Next time we shall see how to do something similar to this in python, or will we ?

monkey_juggle