Go言語12
context使用説明
主な機能:制御タイムアウト時間 コンテキストデータ を保存する.
context処理によるタイムアウト
きほんぶんぽうこうぞう
例-Webサイトへのアクセスタイムアウト
ここでは最下位のrequestでGETリクエストを送信します.&http.Client{}の構造体自体にもTimeoutの設定があり、デフォルトの0値はタイムアウトを設定しないことです.さらにClinetはそのTransportがCancelRequestメソッドを実装しなければならないことを要求し、デフォルトのTransportにはこのメソッドがある.次の例は、最下位の論理をシミュレーションし、タイムアウト後にTransportを手動で呼び出すCancelRequestメソッドです.
コマンド・ラインは、要求されたサーバ・アドレスとして最初のパラメータを受信し、結果を実行します.
1回のリクエストがタイムアウトし、1回に結果が返されます.
コンテキストをcontextで保存する
contextを使用すると、いくつかのカスタムパラメータ伝達もできます.key-valueの形式でcontextの変数に格納し、使うときに取り出します.次の例ではcontextを試して変数を渡します.
ここではただ使い方を示すだけで、例の中のこのような明確な変数は、伝統的な方法で伝えるべきです.グローバルに必要な変数はcontextを使用してメンテナンスできます.contextを使うと、変数の情報が隠されてしまうので、コードの読みやすさが悪くなります.しかも変数のタイプが隠されていてgoの習慣にも合わないので、上で使う前に、タイプ断言をしていました.
contextでgoroutineを終了
context.WithCancelメソッドでは、goroutineのライフサイクルも制御できます.
ここでは2つの変数,ctx,cancelを定義した.cancelは呼び出し可能な関数で、呼び出しが実行された後です.ctx.Doneというパイプで値を取り出すことができます.goroutineではこのパイプでgoroutineの脱退を制御できます.完全な例は次のとおりです.
上記の例の適用シーンは、goroutineを有効にしてタスクを実行し、このgoroutineが終了したことを通知する必要がある場合、ここのcontextで実現できます.
DeadLineタイムアウト
WithDeadlineとWithTimeoutは似ています.いずれも設定により、ある時間に自動的にトリガーされます.ctx.Done()は値を取得できます.違いは、DeadLineは時点を設定し、時間が合えば期限が切れることです.Timeoutは、数秒などの時間を設定し、この時間が過ぎるとタイムアウトします.実は下層のTimeoutもDeadlinによって実現されており、Timeoutでは直接return WithDeadline(parent,time.Now().Add(timeout)).次の例では、50ミリ秒のタイムアウトを設定し、WithDeadlineで設定します.実行プログラムはコマンドラインパラメータを受信し、整数を入力します.このミリ秒の時間が経過すると、カスタムコンテンツが出力されます.数値が大きい(50より大きい)とタイムアウトし、contextのErrメソッドが返す情報が出力されます.
この例はTimeoutを使うほうが便利ですが、ここでは主にDeadlineの使い方を示します.2つの方法の効果は同様で,実際の要求に基づいて適切な方法を選択する.TImeoutはより使いやすいはずなので、Deadineをさらにカプセル化し、より多くのアプリケーションシーンで使用するためのTimeoutメソッドを提供します.
sync.WaitGroupの紹介
これまではパイプを介してgoroutineとデータを伝達していたが,パイプを介して待機を実現することもできた.しかし、goroutineの実行が完了するのを待つだけでは、まだ実現できる方法があります.sync.WaitGroupを使用することで、goroutineのセットが終わるのを簡単に待つことができます.具体的には、次の3つのステップです. Addメソッドを使用して待機数を設定し、カウントに1 を加算する. Doneメソッドを使用して待機数を設定し、カウントを1 減らします.待機数が0に等しい場合、Waitメソッドは を返す.
例1
次はhttpリクエストの例です.すべてのリクエストが戻ってくるまでメイン関数を終了しません.
例2
次の例では、別のスタイルの書き方を示します.
小結
注意:Addメソッドはgoroutineに入れてはいけません.問題ないように見えますが、goroutineが実行されるのを待っていない可能性があります.メイン関数はWaitに実行されます.カウントがまだ始まっていない0の効果で、主関数は実行され続けます.メイン関数が先にWaitを実行するかgoroutineが先にAddを実行するかは運次第でパイプより使いやすく理解しやすく、goroutineのセットを待つことができます.この方法は,goroutine実行の終了を待つ主関数しか実現できない.goroutineの終了を通知する必要がある場合は、パイプで実現します.パイプはインタラクティブなデータに使用できるので、すべての状況が適用されます.sync.WaitGroupシーンは単一ですが、より使いやすいです.
主な機能:
context処理によるタイムアウト
きほんぶんぽうこうぞう
import ""context""
//
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
//
select {
case
例-Webサイトへのアクセスタイムアウト
ここでは最下位のrequestでGETリクエストを送信します.&http.Client{}の構造体自体にもTimeoutの設定があり、デフォルトの0値はタイムアウトを設定しないことです.さらにClinetはそのTransportがCancelRequestメソッドを実装しなければならないことを要求し、デフォルトのTransportにはこのメソッドがある.次の例は、最下位の論理をシミュレーションし、タイムアウト後にTransportを手動で呼び出すCancelRequestメソッドです.
package main
import (
"context"
"fmt"
"os"
"io/ioutil"
"net/http"
"time"
)
type Result struct {
r *http.Response
err error
}
func process(host string) {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel() // cancel ,
c := make(chan Result)
tr := &http.Transport{}
client := &http.Client{Transport: tr}
req, err := http.NewRequest("GET", "http://" + host, nil)
if err != nil {
fmt.Fprintf(os.Stderr, "HTTP GET ERROR: %v
", err)
return
}
go func() {
resp, err := client.Do(req)
c
コマンド・ラインは、要求されたサーバ・アドレスとして最初のパラメータを受信し、結果を実行します.
PS H:\Go\src\go_dev\day12\context> go run main.go google.com
Timeout... Get http://google.com: net/http: request canceled while waiting for connection
PS H:\Go\src\go_dev\day12\context> go run main.go baidu.com
Server Response:
PS H:\Go\src\go_dev\day12\context>
1回のリクエストがタイムアウトし、1回に結果が返されます.
コンテキストをcontextで保存する
contextを使用すると、いくつかのカスタムパラメータ伝達もできます.key-valueの形式でcontextの変数に格納し、使うときに取り出します.次の例ではcontextを試して変数を渡します.
package main
import (
"fmt"
"context"
)
func process(ctx context.Context) {
age, ok := ctx.Value("age").(int) //
if !ok {
age = 18 // , 18
}
fmt.Println("Age:", age)
name, _ := ctx.Value("name").(string) //
fmt.Println("Name:", name)
fmt.Println(ctx.Value("gender")) // ctx ,
fmt.Println(ctx.Value("gender1")) // key, nil
}
func main() {
ctx := context.WithValue(context.Background(), "name", "Adam") // , 2 key value
ctx = context.WithValue(ctx, "age", 23) // , ctx,
ctx = context.WithValue(ctx, "gender", "Male")
process(ctx) // , ctx ,
}
ここではただ使い方を示すだけで、例の中のこのような明確な変数は、伝統的な方法で伝えるべきです.グローバルに必要な変数はcontextを使用してメンテナンスできます.contextを使うと、変数の情報が隠されてしまうので、コードの読みやすさが悪くなります.しかも変数のタイプが隠されていてgoの習慣にも合わないので、上で使う前に、タイプ断言をしていました.
contextでgoroutineを終了
context.WithCancelメソッドでは、goroutineのライフサイクルも制御できます.
//
ctx, cancel:= context.WithCancel(context.Background())
defer cancel()
// cancel() ,ctx.Done()
go func() {
// , context ,
for {
select {
case
ここでは2つの変数,ctx,cancelを定義した.cancelは呼び出し可能な関数で、呼び出しが実行された後です.ctx.Doneというパイプで値を取り出すことができます.goroutineではこのパイプでgoroutineの脱退を制御できます.完全な例は次のとおりです.
package main
import (
"time"
"fmt"
"context"
)
func test() {
// ,
gen := func(ctx context.Context)
上記の例の適用シーンは、goroutineを有効にしてタスクを実行し、このgoroutineが終了したことを通知する必要がある場合、ここのcontextで実現できます.
DeadLineタイムアウト
WithDeadlineとWithTimeoutは似ています.いずれも設定により、ある時間に自動的にトリガーされます.ctx.Done()は値を取得できます.違いは、DeadLineは時点を設定し、時間が合えば期限が切れることです.Timeoutは、数秒などの時間を設定し、この時間が過ぎるとタイムアウトします.実は下層のTimeoutもDeadlinによって実現されており、Timeoutでは直接return WithDeadline(parent,time.Now().Add(timeout)).次の例では、50ミリ秒のタイムアウトを設定し、WithDeadlineで設定します.実行プログラムはコマンドラインパラメータを受信し、整数を入力します.このミリ秒の時間が経過すると、カスタムコンテンツが出力されます.数値が大きい(50より大きい)とタイムアウトし、contextのErrメソッドが返す情報が出力されます.
package main
import (
"context"
"fmt"
"time"
"os"
"strconv"
)
func main() {
n, _ := strconv.Atoi(os.Args[1]) // ,
d := time.Now().Add(50 * time.Millisecond) // 50
ctx, cancel := context.WithDeadline(context.Background(), d) // Timeout, Add
defer cancel()
select {
case go run main.go 123
context deadline exceeded
PS H:\Go\src\go_dev\day12\context\deadline> go run main.go 12
PS H:\Go\src\go_dev\day12\context\deadline>
*/
この例はTimeoutを使うほうが便利ですが、ここでは主にDeadlineの使い方を示します.2つの方法の効果は同様で,実際の要求に基づいて適切な方法を選択する.TImeoutはより使いやすいはずなので、Deadineをさらにカプセル化し、より多くのアプリケーションシーンで使用するためのTimeoutメソッドを提供します.
sync.WaitGroupの紹介
これまではパイプを介してgoroutineとデータを伝達していたが,パイプを介して待機を実現することもできた.しかし、goroutineの実行が完了するのを待つだけでは、まだ実現できる方法があります.sync.WaitGroupを使用することで、goroutineのセットが終わるのを簡単に待つことができます.具体的には、次の3つのステップです.
例1
次はhttpリクエストの例です.すべてのリクエストが戻ってくるまでメイン関数を終了しません.
package main
import (
"os"
"sync"
"fmt"
"net/http"
)
var wg sync.WaitGroup
func main() {
var urls = []string {
"baidu.com",
"51cto.com",
"go-zh.org",
}
for _, url := range urls {
wg.Add(1) // goroutine, 1
go func(url string) {
defer wg.Done() // 1
resp, err := http.Head("http://" + url)
if err != nil {
fmt.Fprintf(os.Stderr, "%s Head ERROR: %v", url, err)
return
}
fmt.Println(*resp)
}(url)
}
wg.Wait() // , ,
fmt.Println("All Requests Down")
}
例2
次の例では、別のスタイルの書き方を示します.
package main
import (
"fmt"
"sync"
"time"
)
func calc(w *sync.WaitGroup ,i int) {
defer w.Done()
fmt.Println("calc:", i)
time.Sleep(time.Second)
}
func main() {
// wg ,
// ,
wg := sync.WaitGroup{}
wg.Add(10) // 10, for 1
for i := 0; i < 10; i++ {
go calc(&wg, i) // ,
}
wg.Wait()
fmt.Println("All goroutine Done")
}
小結
注意:Addメソッドはgoroutineに入れてはいけません.問題ないように見えますが、goroutineが実行されるのを待っていない可能性があります.メイン関数はWaitに実行されます.カウントがまだ始まっていない0の効果で、主関数は実行され続けます.メイン関数が先にWaitを実行するかgoroutineが先にAddを実行するかは運次第でパイプより使いやすく理解しやすく、goroutineのセットを待つことができます.この方法は,goroutine実行の終了を待つ主関数しか実現できない.goroutineの終了を通知する必要がある場合は、パイプで実現します.パイプはインタラクティブなデータに使用できるので、すべての状況が適用されます.sync.WaitGroupシーンは単一ですが、より使いやすいです.