以下は、golang チュートリアル に関するコラムからの Golang の詳細な紹介です。チャンネルChan.、困っている友達のお役に立てれば幸いです!
まず、golang では goroutine とも呼ばれるスレッドを見てみましょう。
この記事を読む前に、並行性と並列性を理解する必要があります。 Golang のスレッドは並列メカニズムではなく、同時実行メカニズムです。両者の違いについてはインターネットで検索すると、多くの紹介情報がネット上にあります。
最初に例を見てみましょう
import( "fmt" ) funcmain(){ go fmt.Println("1") fmt.Println("2") }
golang では、go キーワードの後に関数を使用してスレッドを作成できます。次の関数は、既に記述された関数であることも、匿名関数であることもできます。
funcmain(){ var i=3 go func(a int) { fmt.Println(a) fmt.Println("1") }(i) fmt.Println("2")}
上記のコードは、匿名関数を作成し、パラメータ i (以下の括弧内) も渡します。は実パラメータ、a は仮パラメータです。
では、上記のコードは期待どおりに 1、2、および 3 を出力できるでしょうか?いいえ、プログラムは 2 つしか出力できません。正しいコードを以下に掲載します
import( "fmt" "time" )funcmain(){ var i = 3 go func(a int) { fmt.Println(a) fmt.Println("1") }(i) fmt.Println("2") time.Sleep(1 * time.Second)}
最後にメインスレッドを 1 秒スリープさせるコード行を追加しました。プログラムは 2、3、1 を順番に出力します
。
では、なぜこのようなことが起こっているのでしょうか?プログラムは最初にメイン スレッドを実行するため、メイン スレッドの実行が完了するとプログラムはすぐに終了し、子スレッドを実行するための余分な時間は残りません。プログラムの終了時にメインスレッドを 1 秒間スリープ状態にすると、プログラムは子スレッドを実行するのに十分な時間を確保できます。
まずスレッドがここにあります。チャンネルを見てみましょう。
チャネルはチャネルとも呼ばれ、その名前が示すように、チャネルの機能は複数のスレッド間でデータを転送することです。
バッファなしチャネルの作成
chreadandwrite :=make(chan int)
chonlyread := make(<-chan int) //読み取り専用チャネルの作成
chonlywrite := make(chan<- int) //書き込み専用チャネルを作成します
例を見てみましょう:
ch :=make(chan int) ch <- 1 go func() { <-ch fmt.Println("1") }() fmt.Println("2")
このコードを実行するとエラーが発生します: 致命的なエラー: すべてgoroutines are sleep - Deadlock!
このエラーは、スレッドがデッドロックでスタックし、プログラムの実行を続行できないことを意味します。では、このエラーの原因は何でしょうか?
バッファなしチャネルを作成し、そのチャネルに値を割り当てましたが、割り当てが完了した後、プログラムがデッドロックに陥りました。チャネルはバッファされていない、つまり同期しているため、割り当てが完了した後、チャネルを読み取る前にプログラムはブロックされます。ここで非常に重要な概念があります: チャネル メカニズムは先入れ先出しです。チャネルに値を割り当てる場合は、その値を読み取る必要があります。そうしないとブロッキングが発生します。もちろん、これはバッファリングされていないチャネルに対してのみ有効です。 。バッファリングされたチャネルの場合、送信者はデータがバッファにコピーされるまでブロックします。バッファがいっぱいの場合、送信者は受信者がデータを削除した後でのみブロック状態から回復できます。
上記の例には 2 つの解決策があります:
1. チャネルにバッファを追加し、プログラムの最後にメイン スレッドを 1 秒間スリープさせます。コードは次のとおりです。次のように:
ch :=make(chan int,1) ch <- 1 go func() { v := <-ch fmt.Println(v) }() time.Sleep(1 * time.Second) fmt.Println("2")
この場合、プログラムは 1 と 2 を順番に出力します
2. コードの ch<-1 行をサブスレッド コードの後ろに置きます。
ch :=make(chan int) go func() { v := <-ch fmt.Println(v) }() ch <- 1 fmt.Println("2")
ここでは必要ありません。メインスレッドでチャネルに値が割り当てられた後、メインスレッドはチャネルの値が取り出されるまでブロックされるため、メインスレッドをスリープさせます。子スレッドで。
最後に、プロデューサーとコンシューマーの例を見てみましょう:
import ( "fmt" "time")func produce(p chan<- int) { for i := 0; i < 10; i++ { p <- i fmt.Println("send:", i) } }func consumer(c <-chan int) { for i := 0; i < 10; i++ { v := <-c fmt.Println("receive:", v) } }func main() { ch := make(chan int) go produce(ch) go consumer(ch) time.Sleep(1 * time.Second) }
このコードでは、チャンネルがバッファリングされていないため、プロデューサーがチャンネルに値を割り当てると、プロデューサーはスレッドは、コンシューマ スレッドがチャネルからデータを取り出すまでブロックされます。コンシューマが初めてデータを取り出した後、プロデューサがまだデータを格納していないため、次のサイクルでもコンシューマのスレッドはブロックされ、このときプログラムはプロデューサのスレッドを実行します。このようにして、プログラムはループが終了するまでコンシューマー スレッドとプロデューサー スレッドの間で継続的に切り替えを行います。
バッファリングを使用した別の例を見てみましょう:
import ( "fmt" "time")func produce(p chan<- int) { for i := 0; i < 10; i++ { p <- i fmt.Println("send:", i) } }func consumer(c <-chan int) { for i := 0; i < 10; i++ { v := <-c fmt.Println("receive:", v) } }func main() { ch := make(chan int, 10) go produce(ch) go consumer(ch) time.Sleep(1 * time.Second) }
このプログラムでは、バッファーは int 型の整数を 10 個保存できます。プロデューサー スレッドの実行時に、スレッドはブロックされずに保存されます。一度に 10 個の整数をチャネルに入力し、読み取り時にも一度に読み取ります。
以上がGolangチャンネルChanの詳しい説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。