ホットリロードツールの作り方(GoCon 2021 Spring)
去る4月24日に開催されたGo Conference Online 2021 Springにて、 「ホットリロードツールの作り方」という発表をしました。
当日の動画もYoutubeでご覧いただけます。
また、発表中で紹介している自作ホットリロードツール「arelo」もGitHubで公開しています。
発表の補足:容量1チャネルを使ったトリック
当日質問も頂いたのですが、少しわかりにくかったと思うので補足します。 また、ちょうど裏番組だったセッションでも似たような方法が チャネルの活用方法のひとつとして紹介されていました。
trg := make(chan struct{}, 1)
go func() {
for {
<-trigger
select { // (1)
case trg <- struct{}{}:
default:
}
}
}()
for {
cmd := runCmd()
select { // (2)
case <-trg:
default:
}
<-trg // (3)
<-time.NewTimer(delay).C
killCmd(cmd)
}
まずチャネルtrg
についてです。
容量(capacity)1で初期化しています。
続いて前半のgoroutineでは、チャネルtrigger
にメッセージが届いたら(1)のselect
文でtrg
へ空のstruct
の投入を試みています。
このときtrg
は容量が1なので空のときは投入できますが、すでに1つ入っているときはdefault
に処理が移り何も投入せず、結果としてメッセージは捨てられます。
次に後半のループでは、コマンドを起動した後(3)でtrg
チャネルにメッセージが届くのを待ち、
メッセージが届いたら一定時間待ってコマンドを停止することを繰り返しています。
このなかでtrg
チャネルを待つ前(3)の前に、(2)のselect
文でtrg
チャネルからメッセージを取り出そうと試みています。
もしtrg
にメッセージが入っているなら取り出され、空ならdefault
によりなにもしません。
trg
は容量1なので、高々1つしかメッセージは入っていません。
つまり、(2)のselect
によりtrg
は確実に空になり、(3)で待機することになります。
前半のgoroutineでは、trg
が空の時しかメッセージを投入できませんでした。
そして、後半のループではtrg
が必ず空の状態で(3)の待機をしています。
これらにより、(3)で待ち始めてから最初のtrigger
へのメッセージだけが(3)によって取り出され、
次にまた(3)で待ち始めるまでのメッセージは捨てることになります。
まとめ
裏番組でも言及されていましたが、Goのチャネルはselect
と組み合わせることで真価を発揮します。
そしてContextやタイマーもチャネルになっています。
select
という糊でこれらをうまく連携させていると、いかにもGoらしいコードだなと個人的には感じます。
ところで、容量付きチャネルとselect-default
を組み合わせるテクニックはとても有用で色々な場面で使われていると思うのですが、なにか名前は付いていないのでしょうか。
知っていたら教えてください。