ホームページ > バックエンド開発 > Golang > Go の同時プログラミングについて話しましょう (1)

Go の同時プログラミングについて話しましょう (1)

咔咔
リリース: 2021-07-07 16:36:55
オリジナル
2227 人が閲覧しました

Go の goroutine とチャネルについて話しましょう

  • 序文
  • 1. Goroutine
    • 定義
    • まず goroutine の使い方を知るためにケースを見てください
    • それは何ですか
  • 2. チャネル
    • 基本的な使い方
    • チャネルをパラメータとして渡す
    • 複数のチャネルを作成する
    • チャネルを戻り値として使用する
    • バッファ チャネル
    • チャネル クローズ

    おすすめ関連記事: 「 Go の同時プログラミングの話 (2)

    ##まえがき

##以前 Go 言語を学習していたとき、Groutin と Channel を見たときにスキップしてしまいました。

当時はまったく真剣に考えていませんでしたが、なぜそんなに複雑だと思いますか? (当時の心境)

最近goの同時プログラミングを調べていたら、全部この内容を使っていることが分かり、仕方なく噛んでみました。弾丸ですが、見てみると、それほど難しいものではないことがわかります。

見たくないものを先に置いて、注意を集中してから見ると、思わぬ利益が得られることがあります。

今日の記事は簡単な説明です。カカさんも囲碁コースに登録しています。どのコースを見れば理解が深まりますか?続きはまた後ほど。深さを追加しました。 。

##定義

#関数の前に go を追加するだけです。
    定義上、非同期関数かどうかを区別する必要はありません。
  • 適切な時点でスケジューラが切り替わります。ポイントはたくさんあります。 , ここはあくまで参考であり、切り替わる保証はありませんし、他のところで切り替わらないという保証もありません。 IO 操作、チャネル、ロックの待機、関数呼び出し、runtime.Gosched() など。 。 。
  • race を使用してデータ アクセスの競合を検出する
まずケースを見て goroutine の使用方法を確認してください

最初にケースを見てみましょう

Go の同時プログラミングについて話しましょう (1)

##このケースは、単純な同時実行コードです。その中のキーワードは 1 つだけです。「go」です。

それでは、このコードが何を出力するかを見てみましょう

Go の同時プログラミングについて話しましょう (1)

上の図からわかるように、このコード行は何も出力せず、直接終了します。

直接終了する理由は、コード内の main と fmt の出力が同時に実行されるためです。fmt がデータが来る前に緊急にデータを出力する場合、外側のループはすでに終了しています。直接出た。

Go 言語で! main 関数が終了した後、すべてのゴルーチンを直接 kill することを想定しているため、ゴルーチンが来る前に緊急の印刷データが返されるという現象が発生します。

それでは、印刷されたデータをどのように確認できるか疑問に思われていますか?実際、これは非常に簡単で、main 関数の実行後に急いで終了せず、しばらく待つだけです。ケースを見てください。

Go の同時プログラミングについて話しましょう (1)

#今回の目的の結果が表示されます。

この場合、オープンされるゴルーチンの数は 10 ですが、これを 1000 に変更するとどうなりますか?

結果表示は、1,000 人が同時に印刷しているのと同じように、引き続き正常に表示されます。

では、設定 10 と 1000 にはどのような関係があるのでしょうか?

オペレーティング システムに詳しい人なら、10 個のスレッドを開いても問題はなく、100 個のスレッドを開いても大きな問題はなく、ほぼ十分であることがわかるはずです。

一般に、システムは数十のスレッドを開くことができますが、1,000 人が同時に 1 つのことを実行する必要がある場合、スレッドを使用して問題を解決することはできず、非同期メソッドを使用する必要があります。が必要です。

#しかし、Go 言語では! go キーワードを直接使用するだけで、同時に実行できます。

次に、なぜ go が同時に 1000 件も印刷できるのかについて説明します。

とはまず、コルーチンとスレッドの違いを見てみましょう。

コルーチンは、軽量スレッド非プリエンプティブ マルチタスク、コルーチンは制御権をアクティブに引き継ぎますとして理解できます。

スレッドはいつでもオペレーティング システムによって切り替えられるため、スレッドはプリエンプティブ マルチタスクであることを誰もが知っておく必要があります。ステートメントが実行された場合でも、スレッドには制御がありません。スレッドの半分は実行されます。オペレーティング システムによって切断され、操作のために他のスレッドに転送されます。

逆に、コルーチンの場合、いつ制御権を渡すか、いつ制御権を渡さないかは、コルーチンによって内部的に能動的に決定されます。プリエンプション式なので軽量と呼ばれます。

そして 複数のコルーチンは 1 つ以上のスレッドで実行できます

2. チャンネル

最初のセクションでは、多くのゴルーチンを go で開くことができることを学びました。そのため、ゴルーチン間の双方向チャネルは channel

Go の同時プログラミングについて話しましょう (1)

基本的な使い方

Go の同時プログラミングについて話しましょう (1)

##上の図よりこの場合、make 関数を直接使用してチャネルを作成できることがわかります。

7 行目と 8 行目は、チャネルにデータを送信します。

#それでは、この事件は実行できるでしょうか?試してみよう######

Go の同時プログラミングについて話しましょう (1)

#この時点でエラーが報告されていることがわかりますが、このエラーはチャネルに 1 を送信するときにデッドロックが発生することを意味します。

次に、前の図に戻ります。

Go の同時プログラミングについて話しましょう (1)

上で述べたように、チャネルは goroutine と goroutine の間の相互作用です。

ただし、この場合、不足している goroutine は 1 つだけなので、それを受け取るには別の goroutine が必要です。

これで、Goroutine の開始方法がわかるはずです。

Go の同時プログラミングについて話しましょう (1)

上の図では、新たに別の goroutine を開き、無限ループを使用してチャネルから送信された値を受け入れ、出力しました。 。

しかし、チャネルには 2 つのデータを送信しましたが、このときの印刷結果は 1 つのデータしかないことがわかります。でも、最初のものよりは良くなりましたね!

では、なぜこのようなことが起こるのでしょうか?

コードの実行フローは、まずチャネルに 1 が送信され、ループで最初の値が取得されて出力されることがわかります。

次に、データ 2 をチャネルに送信しますが、印刷する直前に終了します。その結果、データ 1 だけが表示され、データ 2 が表示されない現象が発生します。

Kaka の説明を読めば、この問題の解決方法がすでにわかるはずです。

つまり、関数channelDomeに遅延終了時間を追加することです。

Go の同時プログラミングについて話しましょう (1)

上でわかるように、 go の後にクロージャ関数が続きます。このクロージャで使用される c は、使用される外側の c です。

それでは、パラメータを使用してこの c を渡すことは可能でしょうか?答えは「はい」です。

Go の同時プログラミングについて話しましょう (1)

# もちろん、他のパラメータも渡すことができます

Go の同時プログラミングについて話しましょう (1)

上の図から、チャネルだけでなく id パラメータも渡され、コードを直接最適化することもできることがわかります。囲まれた部分、つまりチャネルから値を直接取得します。

複数のチャネルを作成する

Go の同時プログラミングについて話しましょう (1)

From As上図のように、全員が自分のチャンネルを持って配信し、配信後は全員が自分の受信値を受け取り、出力します。

同様に、チャネルにデータを送信するために 26 行目に新しい for ループを追加していることがわかります。

Go の同時プログラミングについて話しましょう (1)

実行結果から、receive i と accept I の 2 つの値が表示されるなど、出力順序が混乱していることがわかります。

現時点で何か疑問がありますか? チャネルにデータを送信するときは、順番に送信します。次に、受信時に順番に受信する必要があります。

データは順番に送信されていると確信しているため、この問題は Printf でのみ発生する可能性があります。

Printf には IO があり、ゴルーチンでスケジュールされているため、この時点の Printf は故障していますが、受信した値は 1 つずつ出力されます。

チャネルを戻り値として使用する

前のセクションのケースはすべてチャネルによって作成され、使用されます。渡されるパラメータとして。

次に、このセクションは戻り値としてチャネルを返します。

Go の同時プログラミングについて話しましょう (1)

ソースコード

package mainimport (
	"fmt"
	"time")func createWorker(id int) chan int {
	c := make(chan int)
	go func() {
		for {
			fmt.Printf("Worker %d receive %c\n", id, 
ログイン後にコピー

ここから、次のことがわかります。この関数内でチャネルが直接作成されるため、この関数は createWorker 関数に変更されました。

次に、チャネルによって受信された値がコルーチンを通じて出力されます。

#チャンネルを返します。

実行結果を見てみましょう

Go の同時プログラミングについて話しましょう (1)

実行結果から、コードの記述がまだ正しいことがわかりますが、この時点で返されたチャネルの使用方法が非常に直感的にわかることがわかります。

しかしコードの数が大きい場合、このチャネルの使用方法がまったくわからないため、このコードすべてを単純に変更する必要があります。

では、外部の人に使い方を伝える必要があります。

Go の同時プログラミングについて話しましょう (1)

上記のコードから、データがチャネルに送信され、その後 createWorker# で送信されることがわかります。 # #メソッドによって返されるチャネルには、

Go の同時プログラミングについて話しましょう (1)

# とマークする必要があります。

現在のコードは次のようになり、createWorker メソッドの戻り値チャネルの方向を直接マークします。機能はデータを送信することです。

次に、印刷すると、それが領収書になるため、非常に直感的に見えます。

上記の 2 つの手順を変更すると、createWorker がエラーを呼び出すことがわかります。Cannot use 'createWorker(i)' (type chanエラーが表示されると、2 つの型が等しくないことがわかります。

Go の同時プログラミングについて話しましょう (1)

#修正後は、コンパイルが正しく行われ、エラー メッセージが報告されないことがわかります。

Go の同時プログラミングについて話しましょう (1)也是ok的。

Go の同時プログラミングについて話しましょう (1)

本小节源码

package mainimport (
	"fmt"
	"time")func createWorker(id int) chan
ログイン後にコピー

buffer channel

学习了这么久了,那么咔咔问你一个问题,这段代码执行会发生什么?

Go の同時プログラミングについて話しましょう (1)

はい、記事の冒頭で述べたように、チャネルにデータを送信するには、別のコルーチンを開いてデータを受信する必要があるため、エラーが発生します。

コルーチンは軽量と言われていますが、データを送信した後、データを受信するためにコルーチンを切り替える必要があり、非常にリソースを消費します。

それでは、このセクションではこれについて説明します。

buffer channel

3 つのバッファを持つことができるチャネルを作成し、3 つのデータをそのチャネルに送信します。

同時に実行した結果から、デッドロックが発生していないこともわかります。

あなたに質問です。データ 4 をバッファに送信するとどうなりますか?

Go の同時プログラミングについて話しましょう (1)

あなたは賢いです、結果を考えたはずです、はい、報告しましたデッドロック

次に、前のワーカーを使用してチャネル データを受信します。

Go の同時プログラミングについて話しましょう (1)

しかし、実行結果では、送信された 1、2、3、4 が出力されないことがわかります。 。

この質問はこれまでに何度も聞かれていますが、この状況をどのように解決すべきかを自問してみてください。

Go の同時プログラミングについて話しましょう (1)

遅延時間を追加するだけです ちなみに、前の例では文字が出力されたことを説明したいと思います。 、%c はすべての書式設定に使用されますが、数値を出力するようになったので、%d に変更されました。これは小さな変更です。

この方法でチャネルを確立すると、パフォーマンスの向上に一定の効果があります。

これまでに問題を発見しましたか。つまり、チャネルを送信するときに、いつ送信されたかわからないという問題です。

次に、この問題を見てみましょう。

#チャンネルは閉鎖されました

前のケースからコードを借用して説明を続けます。

Go の同時プログラミングについて話しましょう (1)
前のコードと矛盾するのは、最後に close を追加したことです。close は送信側でクローズされることに注意してください。

1、2、3、4 は受信されていますが、実行結果が満足のいくものではないことがわかります。

ただし、以下では多数の 0 が受信されましたが、スクリーンショットにキャプチャされたデータは 1 つだけでした。

送信者がチャネルを閉じても、ワーカーはチャネルが閉じられてもデータを受信します。これは、チャネルが閉じられた後にデータが受信されなくなるという意味ではありません。

しかし、送信者がチャネルを閉じるように設定すると、受信したデータはすべて 0 になります。つまり、ワーカー メソッドによって渡されるパラメータ c chan int の値は 0 になります。

これで、チャネルの型は int になり、0 を受け取ります。次に、文字列型の場合、受信されるのは空の文字列です。

これにはどのくらい時間がかかりますか?それが私たちが設定した 1 ミリ秒の時間です。

このプログラムを変更するように頼まれた場合、何かアイデアはありますか?わからない場合は、このクリックのリズムに合わせて揺れてください。

Go の同時プログラミングについて話しましょう (1)

#関数ワーカーでは、受信に 2 つの値が使用されます。n は渡されるチャネル c です。 ok は、値が存在するかどうかを判断することです。

実行結果が表示され、0 データを受信することはなくなります。

この書き方以外にも、もっと簡単な方法があります。

Go の同時プログラミングについて話しましょう (1)

学習への粘り強さ、執筆への粘り強さ、そして共有への粘り強さは、Kaka が設立以来常に堅持してきた信念です。巨大なインターネット上の Kaka の記事が少しでもお役に立てれば幸いです。カカです、また会いましょう。

以上がGo の同時プログラミングについて話しましょう (1)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
go
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート