Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do nothing when no channel is ready to be read?

Tags:

go

channel

Let's take this example from the GoTour, as it illustrates my problem with processing SDL events only when there are events.

package main

import (
"fmt"
"time"
)

func main() {
tick := time.Tick(1e8)
boom := time.After(5e8)
for {
    select {
    case <-tick:
        fmt.Println("tick.")
    case <-boom:
        fmt.Println("BOOM!")
        return
    default:
        fmt.Println("    .")
        time.Sleep(5e7)
    }
}
}

This works. But what if I don't want to print or sleep in the default case, but just want to keep looping? I tried this:

    case <-boom:
        fmt.Println("BOOM!")
        return
    default: // Nothing here.
    }
}
}

but it blocks.

I have seen here and there a sentence about goroutines scheduling, but I didn't understand them. So I guess I have two questions:

1) Why does it block?

2) How do I make it do nothing without blocking?

like image 787
Niriel Avatar asked Jan 31 '13 19:01

Niriel


1 Answers

Your Original Example produces this

    .
    .
tick.
    .
    .
tick.
    .
    .
tick.
    .
    .
tick.
    .
    .
tick.
BOOM!

Wheraeas Your Second Example produces this

[process took too long]

The difference is what you did in the default case. A default case is always ready to run so a select with a default statement in it never blocks. The second example runs round the loop continuously choosing one of the branches (case or default) that is ready to run. You are now wondering why the the timer never fires. That is because go routines are not pre-emptively scheduled. So because the loop below never does any IO, the time ticks never fire.

for {
    select {
        // whatever
        default:
    }
}

There are a number of ways of fixing this. Firstly you can put some IO in like you did in your first example. Or you could put a runtime.Gosched() in. Or you could allow the go runtime to use more than one thread with runtime.GOMAXPROCS(2) all of which will work.

The best way IMHO is to leave out the default statement entirely like this. A select without a default statement will block until one of the case statements is ready. If you want to do some background processing (that you were doing in the default statement) then start a goroutine - that is the go way!

In fact I've seen so many problems with default in select statements that I'd be tempted to say to never use them.

like image 97
Nick Craig-Wood Avatar answered Oct 07 '22 01:10

Nick Craig-Wood