このチュートリアルでは、Goベースのアプリケーション内でchannelsを使用する方法を見ていきます。
チャネルは、通信を可能にするGoベースのアプリケーション内のgoroutines
間をリンクするパイプであり、その後、変数への値の渡しを可能にします。
それらは非常に便利で、他のプログラミング言語と比較される最低の大騒ぎとのGoの信じられないほど高性能、非常に同時適用を制作するのを助 これは決してまぐれではなく、thelanguageを設計するとき、コア開発者はtheirlanguage内の並行性をファーストクラスの市民にし、aspossibleで作業するのが簡単になることを望んでいたと判断しました。
並行システムを簡単に作る能力は、そもそも私を言語に引き寄せたものであり、私は言わなければならない、それは今のところ絶対的な光でした。
注-私はあなたがgoroutinesについてもっと学びたい場合は、goroutinesに関する私の他のチュートリアルを見てみることをお勧めします。
このチュートリアルの最後までに、次のようにします:
- チャネル
- の背後にある理論についてしっかりと理解しているチャネル
前提条件
このチュートリアルを完了するには、次の前提条件を満たしてい:
- マシンにGoをインストールする必要があります。
ビデオチュートリアル
ご希望の場合は、このチュートリアルはビデオ形式で利用可能です。
理論
チャネルの考え方は新しいものではなく、Goのconcurrencyfeaturesの多くのように、これらの概念はHoareのcommunicating Sequential Processes(1978)、略してCSP、Dijkstraのguarded commands(1975)のようなものからもたらされている。
しかし、Goの開発者は、プログラマがより良く、より正確で、高度に並行したアプリケーションを作成できるようにするために、これらの概念を可能な限り単純な方法で提示することを彼らの使命としている。
簡単な例
これがGoでどのように機能するかの本当に簡単な例をどのように構築できるかを見てみましょう。 私たちは、最初に離れて行くとanarbitrary、ランダムな値を計算し、と呼ばれるチャネル変数に戻ってそれを渡す関数を作成しますvalues
:
ここで何が起こったのかを見てみましょう。 私たちのmain()
関数では、values := make(chan int)
と呼ばれ、この呼び出しは効果的に新しいチャネルを作成したので、その後CalculateValue
ゴルーチン内で使用することができました。
注-
values
チャンネルをインスタンス化するときは、マップやスライスのように、使用する前にチャンネルを作成する必要があるため、make
を使用しました。
チャンネルを作成した後、defer close(values)
を呼び出し、main()
関数の実行が終了するまでチャンネルの終了を指定しました。 これは、通常、私たちが自分自身の後に整理することを確実にするためのベストプラクティスと考えられています。
defer
への呼び出しの後、単一のゴルーチンを開始します。CalculateValue(values)
新しく作成したvalues
チャネルをパラメータとして渡します。 私たちのCalculateValue
関数内では、1-10の間の単一のrandomvalueを計算し、これを出力し、values <- value
を呼び出してこの値をvalues
チャネルに送信します。
main()
関数に戻り、value := <-values
を呼び出してvalues
チャネルから値を受け取ります。
注-このプログラムを実行すると、すぐに終了しないことに注意してください。 これは、チャネルとの間で送受信する行為がブロックされているためです。 私たちの
main()
関数は、ourchannelから値を受け取るまでブロックします。
このコードを実行すると、出力は次のようになります:
$ go run main.goGo Channel TutorialCalculated Random Value: {} 77
まとめ:
myChannel := make(chan int)
– 型のチャネルであるmyChannelを作成しますint
channel <- value
– チャネルに値を送信する
value := <- channel
– チャネル
から値を受け取るので、Goプログラムでチャネルをインスタンス化して使用すると、これまでのところfairlystraightforwardに見えますが、より複雑なシナ
バッファされていないチャンネル
ゴルーチン内で伝統的なchannel
を使用すると、予期していない動作が発生することがあります。 従来のunbuffered
チャネルでは、あるgoroutineがこのチャネルに値を送信するたびに、そのgoroutineはそのチャネルから値が受信されるまでブロックされます。
これを実際の例で見てみましょう。 以下のコードを見てみると、以前に持っていたコードと非常に似ています。 ただし、CalculateValue()
関数を拡張して、自動的に計算された値をチャネルに送信した後にfmt.Println
を実行しました。
main()
関数では、go CalculateValue(valueChannel)
への2回目の呼び出しを追加したため、thischannelに2つの値が非常に迅速に連続して送信されることを期待する必要があります。
しかし、これを実行すると、最初のgoroutinesのfinalprint文のみが実際に実行されることがわかります:
go run main.goGo Channel TutorialCalculated Random Value: {} 1Calculated Random Value: {} 71Only Executes after another goroutine performs a receive on the channel
この理由は、secondgoroutineでc <- value
の呼び出しがブロックされ、その後、2番目のgoroutine
がそれ自身の実行を完了する機会を得る前にmain()
関数が実行を終了したためです。
Buffered Channels
このブロック動作を回避する方法は、abuffered channelと呼ばれるものを使用することです。 これらのバッファリングされたチャネルは、本質的には所与のサイズのキューであり、クロスゴルーチン通信に使用することができる。 バッファされていないチャネルではなく、バッファされていないチャネルを作成するには、make
コマンドにcapacityargumentを指定します:
bufferedChannel := make(chan int, 3)
これをバッファリングされたチャネルに変更することにより、送信操作c <- value
は、チャネルがいっぱいになる必要があるgoroutines内でのみブロックします。
バッファされたチャンネルを使用するように既存のプログラムを変更し、出力を見てみましょう。 ゴルーチンが実行を完了できるようにするのに十分なmain()
関数を遅延ブロックするために、main()
関数の下部にtime.Sleep()
呼び出しを追加したことに注意してくださ
さて、これを実行すると、2番目の受信がmain()
関数で呼び出されていないという事実に関係なく、2番目のgoroutineが実行をindeedcontinueすることがわかります。 time.Sleep()
のおかげで、私達はunbufferedチャネルと妨害の性質およびourbufferedチャネルおよびnon-blocking(完全でないとき)性質間の相違をclearlyseeできます。
Go Channel TutorialCalculated Random Value: {} 1Calculated Random Value: {} 77This executes regardless as the send is now non-blockingThis executes regardless as the send is now non-blocking
結論
だから、このかなり長いチュートリアルでは、Go内のチャンネルの様々なdistinctタイプについて学ぶことができました。 私たちは、バッファリングされたチャネルとバッファされていないチャネルの違いと、それらを並行goプログラムの利点にどのように使用できるかを発見しました。
このチュートリアルを楽しんだ場合は、以下のコメントセクションで私に知らせること自由に感じてください。 あなたは私がbetterthenを行うことができるかについての提案があれば、私は以下のコメント欄でそれらを聞くのが大好きです!
さらに読む
この記事を楽しんで、GoでConcurrencyinの作業についての詳細を学びたい場合は、concurrencyに関する他の記事をチェックすることをお勧めします:
- Go Mutex Tutorial
- Go Goroutines Tutorial
- go sync.ウェイトグループチュートリアル