5

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

しかし、それはブロックします。

私はあちこちでゴルーチンのスケジューリングについての文章を見てきましたが、私はそれらを理解していませんでした。だから私は2つの質問があると思います:

1)なぜブロックするのですか?

2)ブロックせずに何もしないようにするにはどうすればよいですか?

4

1 に答える 1

6

あなたの元の例はこれを生成します

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

Wheraeasあなたの2番目の例はこれを生成します

[process took too long]

違いは、あなたがそのdefault場合にしたことです。defaultケースはいつでも実行できる状態になっているためselect、デフォルトのステートメントが含まれているケースがブロックされることはありません。2番目の例は、実行の準備ができているブランチ(ケースまたはデフォルト)の1つを継続的に選択してループを実行します。あなたは今、なぜタイマーが決して発火しないのか疑問に思っています。これは、goルーチンが先制的にスケジュールされていないためです。したがって、以下のループはIOを実行しないため、タイムティックが発生することはありません。

for {
    select {
        // whatever
        default:
    }
}

これを修正する方法はいくつかあります。まず、最初の例で行ったように、いくつかのIOを入れることができます。または、runtime.Gosched()を挿入することも、goランタイムがruntime.GOMAXPROCS(2)で複数のスレッドを使用できるようにすることもできます。これらはすべて機能します。

IMHOの最善の方法は、このようにデフォルトのステートメントを完全に除外することです。デフォルトステートメントのないselectは、caseステートメントの1つが準備できるまでブロックされます。(デフォルトのステートメントで行っていた)バックグラウンド処理を実行したい場合は、ゴルーチンを開始します。これが成功の秘訣です。

実際、selectステートメントのデフォルトには非常に多くの問題があり、決して使用しないように言いたくなります。

于 2013-01-31T20:29:44.530 に答える