Program with select statements escape deadlock in go

Issue

This question have quite possibly been answered by I couldn’t find it so here we go:

I have this go function that sends or recieves “messages”, whichever one is avaivable, using a select statement:

func Seek(name string, match chan string) {
select {
case peer := <-match:
    fmt.Printf("%s sent a message to %s.\n", peer, name)
case match <- name:
    // Wait for someone to receive my message.

I start this function on 4 different go-routines, using an unbuffered channel (It would be better to use a buffer och 1 but this is merely experimental):

people := []string{"Anna", "Bob", "Cody", "Dave"}
match := make(chan string)
for _, name := range people {
    go Seek(name, match, wg)

Now, I’ve just started using go and thought that since we’re using an unbuffered channel, both the send and recieve statement of the “select” should block (there’s no one waiting to send a message so you can’t recieve, and there’s no one waiting to recieve so you can’t send), meaning that there won’t be any communcation done between the functions, aka Deadlock. However running the code shows us that this is not the case:

API server listening at: 127.0.0.1:48731
Dave sent a message to Cody.
Anna sent a message to Bob.
Process exiting with code: 0

My question to you lovely people is why this happens? Does the compiler realize that the functions want to read / write in the same channel and arranges that to happen? Or does the “select” statement continually check if there’s anyone available to use the channel with?

Sorry if the question is hard to answer, I’m still a novice and not that experienced in how things operate behind the scene 🙂

Solution

Now, I’ve just started using go and thought that since we’re using an unbuffered channel, both the send and recieve statement of the “select” should block (there’s no one waiting to send a message so you can’t recieve, and there’s no one waiting to recieve so you can’t send)

This is actually not true; in fact, there are multiple goroutines waiting to receive and multiple goroutines waiting to send. When a goroutine does a select like yours:

select {
case peer := <-match:
    fmt.Printf("%s sent a message to %s.\n", peer, name)
case match <- name:
    // Wait for someone to receive my message.

It is simultaneously waiting to send and to receive. Since you have multiple routines doing this, every routine will find both senders and receievers. Nothing will block. The selects will choose cases at random since multiple cases are unblocked at the same time.

Answered By – Adrian

Answer Checked By – Mary Flores (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.