How to schedule running non-blocking functions

Issue

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

Answered By – Marc

Answer Checked By – Cary Denson (GoLangFix Admin)

Leave a Reply

Your email address will not be published.