Goにおけるタイムアウト制御の解決策
前文
日々の開発の中で、時間のかかるタスクの一括処理、ネットワークリクエストなど、タイムアウト制御のシナリオに遭遇することがあると思います。優れたタイムアウト制御は、いくつかの問題を効果的に回避することができます (例.
goroutine
リーク、リソースの非放出など)。
タイマー
goでタイムアウト制御を実装する方法は非常にシンプルで、まず最初のオプションである
Time.After(d Duration)
:
func main() {
fmt.Println(time.Now())
x := <-time.After(3 * time.Second)
fmt.Println(x)
}
を出力します。
2021-10-27 23:06:04.304596 +0800 CST m=+0.000085653
2021-10-27 23:06:07.306311 +0800 CST m=+3.001711390
time.After()
が返されます。
Channel
その
Channel
は、d時間後にデータを書き込む。
この機能により、いくつかの非同期制御のタイムアウトシナリオを実装することが可能である。
func main() {
ch := make(chan struct{}, 1)
go func() {
fmt.Println("do something... ")
time.Sleep(4*time.Second)
ch<- struct{}{}
}()
select {
case <-ch:
fmt.Println("done")
case <-time.After(3*time.Second):
fmt.Println("timeout")
}
}
ここでは
goroutine
は、select を使用する時間のかかるタスクを実行することで
channel
データを取得した時点で終了するもの。
goroutine
が限られた時間内にタスクを完了しない場合、メインの
goroutine
で、タイムアウトの役割も果たす。
を出力します。
何とかしてくれ
タイムアウト
キャンセル後、チャンネルがメッセージを送信する間、またはチャンネルや他の通知方法を閉じることができます。
チャネルは、ゴルーチンをブロックしてリークを起こさないようなサイズにするのがベストであることに注意してください。
コンテキスト
2つ目の選択肢はコンテキストを使うことで、goのコンテキストは強力です。
を使用する
context.WithTimeout()
メソッドを使用すると、タイムアウト機能を持つコンテキストが返されます。
ch := make(chan string)
timeout, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
go func() {
time.Sleep(time.Second * 4)
ch <- "done"
}()
select {
case res := <-ch:
fmt.Println(res)
case <-timeout.Done():
fmt.Println("timout", timeout.Err())
}
同じ使い方で
context
の
Done()
関数は
channel
その
channel
は、現在のジョブが完了したとき、あるいはコンテキストがキャンセルされたときに有効になります。
timout コンテキストの期限を超えました
By
timeout.Err()
また、現在の
context
が閉じている。
goroutine コンテキストの受け渡し
を使用します。
context
には、複数のゴルーチンに渡って渡される自然な性質を利用して、コンテキストを渡した全てのゴルーチンが同時にキャンセル通知を受け取れるという利点があり、これは複数のゴで非常に広く使われています。
func main() {
total := 12
var num int32
log.Println("begin")
ctx, cancelFunc := context.WithTimeout(context.Background(), 3*time.Second)
for i := 0; i < total; i++ {
go func() {
//time.Sleep(3 * time.Second)
atomic.AddInt32(&num, 1)
if atomic.LoadInt32(&num) == 10 {
cancelFunc()
}
}()
}
for i := 0; i < 5; i++ {
go func() {
select {
case <-ctx.Done():
log.Println("ctx1 done", ctx.Err())
}
for i := 0; i < 2; i++ {
go func() {
select {
case <-ctx.Done():
log.Println("ctx2 done", ctx.Err())
}
}()
}
}()
}
time.Sleep(time.Second*5)
log.Println("end", ctx.Err())
fmt.Printf("execution finished %v", num)
}
上記の例では、どのような
goroutine
が何重にもネストされている場合は
context
メッセージがキャンセルされたとき(もちろん、このとき
context
を渡さなければならない)
を手動で呼び出すこともできます。
cancelFunc()
関数を使用します。
Ginの場合
Ginでは
Shutdown(ctx)
関数も駆使して
context
.
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err ! = nil {
log.Fatal("Server Shutdown:", err)
}
log.Println("Server exiting")
例えば、上記のコードは、タイムアウトで10秒待つと
Gin
これは、上記の例と同じ原理を実装したものです。
概要
久しぶりに碁を書いたので、練習がてら自分のプロジェクトを書きました:インターフェースストレステストツールです。
非常に一般的な要件として、圧力テストのN秒後に終了することが挙げられますが、ここでまた初めて、知識が応用されることになります
go
というところからスタートします。
https://github.com/crossoverJie/ptg/blob/d0781fcb5551281cf6d90a86b70130149e1525a6/duration.go
以上でGoのタイムアウト制御についての記事を終わります。Goのタイムアウト制御については、Script Houseの過去記事を検索するか、引き続き以下の関連記事を参照してください。
関連
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン