-2

I have this piece of code.

func Start() bool {
    for {
        if checkSomthingIsTrue() {
            if err := doSomthing(); err != nil {
                continue
            }
        }
        select {
        case <-ctx.Done():
            return true
        }
    }
}

How to make the above function non blocking without using default: case. reason to not use default case is because its eating up 100% CPU always.

Answer: I have used time.Ticker to throttle Thanks

  • 3
    `select` blocks if none of the communication ops are ready to proceed. You can only make a `select` without `default` non-blocking if you ensure at least one of its comm ops can proceed. – icza Jun 17 '21 at 14:58
  • 5
    If you don't want a `default` because it "eats up" your CPU, then it looks you don't want `checkSomthingIsTrue()` to be called continuously. Use a `time.Timer` or `time.Ticker` to achieve some "sleep" between iterations, and still monitor the context while sleeping. – icza Jun 17 '21 at 15:00
  • 4
    If you don't want a loop to use 100% CPU you want it to block. Can you explain how you want a non-blocking statement to block? – JimB Jun 17 '21 at 15:00
  • There is a literal answer, but it's useless: just make sure one of the channels you select on is always ready. For instance, add a closed channel to the list of channels. But then you'll use 100% CPU on the closed channel. – torek Jun 17 '21 at 16:03

1 Answers1

6

There is a fundamental misunderstanding here. A thread can do only two things:

  • A thread can block, waiting for something.

  • A thread can run, using CPU.

If a thread never blocks, then it uses 100% of the available CPU. You cannot make non-blocking code use less than 100% of the available CPU.

You have three options:

  1. Use non-blocking code, and accept the 100% CPU usage.

  2. Redesign checkSomthingIsTrue() so it uses a channel, and can be put inside the select block.

    for {
        select {
        case <-ctx.Done():
            return true
        case <-whenSomethingIsTrue():
            if err := doSomthing(); err != nil {
                continue
            }
        }
    }
    
  3. Use a timeout to throttle the loop, for example:

    // Poll every 100ms.
    const pollInterval = 100 * time.Millisecond
    for {
        select {
        case <-ctx.Done():
            return true
        case <-time.After(pollInterval):
            if checkSomthingIsTrue() {
                if err := doSomthing(); err != nil {
                    continue
                }
            }
        }
    }
    

Also note that continue makes no sense, but that is a different issue.

Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
  • 1
    we can use [time.Tikcer](https://gobyexample.com/tickers) instead of `time.After` right? – nipuna Jun 17 '21 at 15:09
  • 2
    @nipuna: Sure, if you prefer, you can use `Ticker`. The timing will be different. – Dietrich Epp Jun 17 '21 at 15:10
  • 1
    You can use a `time.Ticker` but you may need to call Stop() on it when you are done otherwise you may leak tickers.`time.After()` cleans itself up after the duration. A `time.Timer` will be the same as `time.After()` for timing but you control when it is cleaned up. – Bracken Jun 17 '21 at 19:31