Problem:
In the Go Tour exercise #71, using go run 71_hang.go nogood results in the program running indefinitely, while go run 71_hang.go ok works as expected. The only difference is the addition of fmt.Print("") in the default case of the select statement.
Explanation:
A default statement in a select changes the behavior of the statement. Without a default, select blocks until there are messages on the channels. With a default, select executes the default statement every time there is nothing to read from the channels.
In the original code, the default statement creates an infinite loop. Since the scheduler cannot schedule other goroutines, the program runs indefinitely.
Solution 1:
Remove the default statement and use a non-blocking select:
for { select { case todo := <-toDoList: if todo.depth > 0 && !visited[todo.url] { crawling++ visited[todo.url] = true go crawl(todo, fetcher, toDoList, doneCrawling) } case <-doneCrawling: crawling-- } if crawling == 0 { break } }
Solution 2:
Keep the default statement but ensure that the goroutine yields. One way to achieve this is by using GOMAXPROCS=2, which allows the scheduler to use multiple cores.
Additional Note:
Goroutines are co-operatively scheduled. Select is a point where a goroutine should yield. However, in the given example, the reason why select doesn't yield without the fmt.Print() statement is not fully understood and requires further investigation.
The above is the detailed content of Why Does a Go `select` Statement Hang Without a `default` Case and How Can It Be Fixed?. For more information, please follow other related articles on the PHP Chinese website!