selectはGolangの制御構造であり、文法的にはswitchステートメントと似ていますが、selectはゴルーチン間の通信に使用される点が異なります。各ケースは、送信または受信のいずれかの通信操作である必要があります。Selectは実行可能なランダムケースを実行します。実行するケースがない場合、実行するケースがあるまでゴルーチンはブロックされます。
複数選択を選択
selectの表現は、golangのselectが通信制御ステートメントであることを除いて、基本的にswitchcaseの表現と同じです。selectの実行には、通信の送信または受信が必要です。そうでない場合、通信はブロックされます。
ch := make(chan bool, 0)
ch1 := make(chan bool, 0)
select {
case ret := <-ch:
fmt.Println(ret)
case ret := <-ch1:
fmt.Println(ret)
}
chとch1に送信する通信データがない場合、selectはchまたはch1に送信するデータがあるまでブロックし、selectは対応するケースを実行してデータを受け入れます。
selectはタイムアウト制御を実装します
selectメカニズムを使用して、単純なタイムアウト制御を実装できます。
プログラムが完全に実行するコードを最初に見てください
func service(ch chan bool) {
time.Sleep(time.Second*3)
ch<-true
}
func main() {
ch := make(chan bool, 0)
go service(ch)
select {
case ret := <-ch:
fmt.Println(ret)
case <-time.After(time.Second*5):
fmt.Println("timeout")
}
}
___go_build_main_go #gosetup
true
5Sはtime.Afterタイムアウトを使用して定義され、サービスプログラムは3Sを実行するため、タイムアウトがあってはなりません。これは予想と一致しています。
タイムアウトの実行をもう一度見てみましょう。サービスプログラムの実行時間を6Sに設定します。タイムアウト制御は引き続き5Sであり、実行効果を確認します。
func service(ch chan bool) {
time.Sleep(time.Second*6)
ch<-true
}
func main() {
ch := make(chan bool, 0)
go service(ch)
select {
case ret := <-ch:
fmt.Println(ret)
case <-time.After(time.Second*5):
fmt.Println("timeout")
}
}
___go_build_main_go #gosetup
timeout
タイムアウトケースの実行は、実際には予想と同じです。
チャネルが閉じているかどうかを判断するために選択します
最初にデータを受信する文法を見てください
val,ok <- ch
ok true 正常接收数据
ok false 通道关闭
実際には、データを受信するための2つのパラメーターがあることがわかります。2番目のブール値は、チャネルが閉じているかどうか、およびデータを正常に受信できるかどうかを反映します。
テストコードを見てください。
データ送信者と2つのデータ受信者を作成しました。送信者がチャネルを閉じると、2つの受信者のゴルーチンは、上記の構文によってチャネルが閉じられているかどうかを判断し、ゴルーチンが終了しているかどうかを判断できます。 。
func sender(ch chan int, wg *sync.WaitGroup) {
for i:=0;i<10;i++ {
ch<-i
}
close(ch)
wg.Done()
}
func receiver(ch chan int, wg *sync.WaitGroup) {
for {
if val,ok := <-ch;ok {
fmt.Println(fmt.Sprintf("%d,%s",val, "revevier"))
} else {
fmt.Println("quit recevier")
break;
}
}
wg.Done()
}
func receiver2(ch chan int, wg *sync.WaitGroup) {
for {
if val,ok := <-ch;ok {
fmt.Println(fmt.Sprintf("%d,%s",val, "revevier2"))
} else {
fmt.Println("quit recevier2")
break;
}
}
wg.Done()
}
func main() {
ch := make(chan int, 0)
wg := &sync.WaitGroup{
}
wg.Add(1)
go sender(ch, wg)
wg.Add(1)
go receiver(ch, wg)
wg.Add(1)
go receiver2(ch, wg)
wg.Wait()
}
の結果
0,revevier2
2,revevier2
3,revevier2
4,revevier2
5,revevier2
6,revevier2
7,revevier2
1,revevier
9,revevier
quit recevier
8,revevier2
quit recevier2
1つのデータ送信者と2つのデータ受信者が表示されます。チャネルが閉じられると、両方のデータ受信者がチャネルが閉じられたという通知を受信します。
閉じたチャネルにデータを送信すると、プログラムはパニックになります。閉じたチャネルからデータを受信すると、参照の意味がないチャネルタイプのゼロ値データを受信します。Intは0、文字列は空です。 ....。
終了タイマーと他のプログラムを選択します
回転タイマーは開発時によく使用されますが、プログラムが終了すると回転タイマーを閉じることができません。実際、selectはこの問題を解決できます。
ローテーションタスクがある場合は、タイマーが必要で、3Sごとにロジックを実行し、10S後にこのタイマーを閉じます。
コードを見てください
func TimeTick(wg *sync.WaitGroup,q chan bool) {
defer wg.Done()
t := time.NewTicker(time.Second*3)
defer t.Stop()
for {
select {
case <-q:
fmt.Println("quit")
return
case <-t.C:
fmt.Println("seconds timer")
}
}
}
func main() {
q := make(chan bool)
wg := new(sync.WaitGroup)
wg.Add(1)
go TimeTick(wg,q)
time.Sleep(time.Second*10)
close(q)
wg.Wait()
}
の結果
seconds timer
seconds timer
seconds timer
quit
チャネルを閉じて、ポーリングタイマーのゴルーチンを正常に終了します。