小编典典

当没有通道可供读取时,怎么办?

go

让我们以GoTour为例,它说明了我仅在有事件时才处理SDL事件的问题。

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)
    }
}
}

这可行。但是,如果我不想在默认情况下打印或休眠,而只想继续循环怎么办?我尝试了这个:

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

但它阻止了。

我在这里和那里看到过有关goroutine调度的句子,但我不理解。所以我想我有两个问题:

1)为什么会阻塞?

2)如何使它不阻塞不执行任何操作?


阅读 289

收藏
2020-07-02

共1个答案

小编典典

您的原始示例产生了这个

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

Wheraeas 您的第二个例子产生了这个

[process took too long]

不同之处在于您在此default案例中所做的。default案例总是可以运行,因此select带有默认语句的案例永远不会阻塞。第二个示例绕循环运行,不断选择准备运行的分支之一(大小写或默认)。您现在想知道为什么计时器永远不会触发。那是因为go例程没有被预先安排。因此,由于下面的循环永远不会执行任何IO,因此时间间隔永远不会触发。

for {
    select {
        // whatever
        default:
    }
}

有许多解决此问题的方法。首先,您可以像在第一个示例中那样放置一些IO。或者,您可以放置​​一个runtime.Gosched()。或者,您可以允许go运行时在runtime.GOMAXPROCS(2)中使用多个线程。

恕我直言,最好的方法是完全忽略默认语句,就像这样。没有默认语句的选择将阻塞,直到其中一个case语句准备就绪。如果要进行一些后台处理(您在默认语句中所做的处理),请启动goroutine-
这是可行的方法!

实际上,我在select语句中已经看到很多默认问题,所以我很想说永远不要使用它们。

2020-07-02