Search code examples
multithreadinggogoroutine

How to schedule running non-blocking functions


My questions is how to schedule running independent non-blocking functions every interval N.

My initial approach is to use go channels within a select statement to receive the values in a non-blocking manner and use time.Sleep(N) in each function to schedule the call.

In the code snippet below, this only for the first run; however, after the first call, it keeps calling computeY() repeatedly without respecting the time.Sleep() call.

    package main

    import (
        "fmt"
        "time"
    )

    var (
        x string = ""
        y string = ""
    )

    func computeY(c chan string) {
        time.Sleep(10 * time.Second)
        fmt.Println("I'm in Y")

        y = "this is Y value"
        c <- y
    }

    func computeX(c chan string) {
        time.Sleep(1 * time.Second)

        x = "this is X value"
        c <- x
    }

    func main() {
        xC := make(chan string)
        yC := make(chan string)

        for {
            go computeX(xC)
            go computeY(yC)

            select {
            case x := <-xC:
                fmt.Println(fmt.Sprintf("X: %v, Y: %v", x, y))
            case y := <-yC:
                fmt.Println(fmt.Sprintf("X: %v, Y: %v", x, y))
            }

        }
    }

Solution

  • You are calling both computeX and computeY every iteration of the loop.

    Since computeX takes 1s, the for loop iterates once per second and an extra time when yC gets a value.

    This means that you're running go computeY at t=0s, t=1s, t=2s, etc.... The first one terminates at t=10s, the second at t=11s, etc...

    If you want to make sure you only schedule one computeX and computeY at a time, you need to change your main to something along the lines of:

        go computeX(xC)
        go computeY(yC)
        for {
            select {
            case x = <-xC:
                fmt.Printf("Finished computeX: X: %v, Y: %v\n", x, y)
                go computeX(xC)
            case y = <-yC:
                fmt.Printf("Finished computeY: X: %v, Y: %v\n", x, y)
                go computeY(yC)
            }
        } 
    

    A few other things to note about your code:

    • x and y are global and assigned in computeX and computeY
    • your channel reads shadow x and y
    • fmt.Println(fmt.Sprintf("...")) is just fmt.Printf("...\n")
    • you don't need to initialize strings to "", that's the default value