こんにちは。KOUKIです。
もう2020年もあと2ヶ月くらいで終わりますが、コロナウイルスのお陰で、外出制限がかかってなかなか遊びに行けないですよね。
今年は、家で大人しく、プログラミングのスキルアップに勤しみましょう!
そんなわけで、私は現在、Goroutineの挙動を色々と研究してます^^
今回の記事では、Go言語のSelect構文とChannelを使って、タイムアウトの実装を行いたいと思います!
<目次>
早速実装
やりたいこととしては、一つのGoroutineから値を受け取る際、時間制限を儲けたいです。
まずは、コードをみてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package main import ( "fmt" "time" ) func main() { ch := make(chan string, 1) go func() { // すげー長い処理 time.Sleep(100 * time.Second) ch <- "Process Done" }() select { case m := <-ch: fmt.Println(m) // Timeout(100秒) case <-time.After(100 * time.Second): fmt.Println("Time Out!") } } |
値受け渡し用のchannel(ch)を一つ作成し、ものすごく時間がかかる処理(time.Sleep)をGoroutineで処理後、処理した結果をSelect構文内の”case m”で受け取っています。
Select構文を使うと上記のコードのように、channelごとに分けて値を取得することが可能です。
そして、ここが肝ですが、Select構文の「case <-time.After(100 * time.Second)」とすると、タイムアウトが実装できます。
time.Afterの実装は「After func(d Duration) <-chan Time」と定義されており、channelを返却します。そのため、Select構文で処理できるわけです。
そして、After関数は、渡した引数分(ここでは100秒)時間を置いてから、結果を返すので、これでタイムアウトを表現しています。
100秒間は少し長いので、以下のようにコードを書き換えてみましょう。
1 2 3 4 5 6 7 8 |
func main() { ... select { // Timeout(1秒) case <-time.After(1 * time.Second): fmt.Println("Time Out!") } } |
この状態でプログラムを実行するとTime Out!と表示されるはずです。
1 2 |
$ go run main.go Time Out! |
逆にタイムアウトを100秒に戻し、100秒かかる処理を1秒に変更するとProcess Doneが表示されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
func main() { ch := make(chan string, 1) go func() { // すげー長い処理 time.Sleep(1 * time.Second) // 1秒に変更 ch <- "Process Done" }() select { case m := <-ch: fmt.Println(m) // Timeout case <-time.After(100 * time.Second): // 100秒に変更 fmt.Println("Time Out!") } } |
1 2 |
$ go run main.go Process Done |
おわりに
Selectとchannelを使ったタイムアウトはかなり便利な機能ですが、まだ実務では使えてません^^
こういう機能は、ファイルを加工するツールとかには使えそうなのですが、Webアプリケーション開発だとなかなか使う機会がないんですよね。
何か便利ツールを考えて、使っていきたいですね。
それではまた!
最近のコメント