購読ありがとうございます

はじめに

Goのチャンネルを初めて使い始めたとき、チャンネルをデータ構造として考えるのは間違いでした。 私はgoroutines間の自動同期アクセスを提供するキューとしてチャネルを見ました。 この構造的な理解により、私は多くの悪く複雑な並行コードを書くことになりました。

私は時間をかけて、チャネルがどのように構造化されているかを忘れ、どのように動作するかに焦点を当てるのが最善であることを学びました。 だから今、それはチャネルに来るとき、私は一つのことについて考えます:シグナリング。 チャンネルは、あるゴルーチンが特定のイベントについて別のゴルーチンに信号を送ることを可能にします。 シグナリングは、チャネルで行うべきすべての中核にあります。 チャネルをシグナリングメカニズムとして考えることで、明確に定義され、より正確な動作でより良いコードを書くことができます。

シグナリングがどのように機能するかを理解するには、その三つの属性を理解する必要があります:

  • 配信の保証
  • 状態
  • データの有無

これらの三つの属性は、シグナリングに関する設計哲学を作成するために連携して動作します。 これらの属性について説明した後、これらの属性を適用したシグナリングを示すいくつかのコード例を提供します。

配信の保証

配信の保証は、”特定のゴルーチンによって送信された信号が受信されたことを保証する必要がありますか?”

つまり、リスト1のこの例を考えると、

Listing1

01 go func() {02 p := <-ch // Receive03 }()0405 ch <- "paper" // Send

送信側のgoroutineは、行05のチャネルを介して送信されているpaperが行02のgoroutineによって受信されたことを保証する必要がありますか?

この質問に対する答えに基づいて、使用するチャンネルの2つのタイプのうち、UnbufferedまたはBufferedを知ることができます。 各チャネルは、配信の保証について異なる動作を提供します。

図1:配達の保証

配達の保証

保証は重要であり、あなたがそう思わないなら、私はあなたを販売したいもののトンを持っています。 もちろん、冗談を言おうとしているのですが、人生に保証がないと緊張しませんか? 並行ソフトウェアを作成する際には、保証が必要かどうかを強く理解することが重要です。 私たちが続けるように、あなたは決定する方法を学びます。

状態

チャネルの動作は、現在の状態に直接影響されます。 チャネルの状態は、nil、open、closedのいずれかになります。

以下のリスト2は、これらの三つの状態のそれぞれにチャネルを宣言または配置する方法を示しています。

2

// ** nil channel// A channel is in a nil state when it is declared to its zero valuevar ch chan string// A channel can be placed in a nil state by explicitly setting it to nil.ch = nil// ** open channel// A channel is in a open state when it's made using the built-in function make.ch := make(chan string) // ** closed channel// A channel is in a closed state when it's closed using the built-in function close.close(ch)

状態は、送信操作と受信操作の動作を決定します。

信号はチャネルを介して送受信されます。

図2:状態

状態

チャネルがnil状態の場合、チャネルで試行された送信または受信はブロックされます。 チャネルがオープン状態のとき、信号を送受信することができます。 チャネルが閉じた状態になると、信号を送信することはできなくなりますが、信号を受信することは可能です。

これらの状態は、遭遇するさまざまな状況に必要なさまざまな動作を提供します。 状態と配送保証を組み合わせると、設計の選択の結果として発生するコスト/便益の分析を開始できます。 多くの場合、チャンネルがどのように動作するかを理解しているため、コードを読むだけですぐにバグを発見することもできます。

データの有無

最後に考慮する必要がある信号属性は、データの有無にかかわらず信号を送信する必要があるかどうかです。

あなたは、チャネル上で送信を実行することにより、データで信号を送ります。

3

01 ch <- "paper"

あなたがデータで信号を送るとき、それは通常次の理由です:

  • goroutineは新しいタスクを開始するように求められています。
  • ゴルーチンが結果を報告する。

チャンネルを閉じることでデータなしで信号を送ります。

4

01 close(ch)

データなしで信号を送るとき、それは通常次の理由です:

  • ゴルーチンは、彼らがやっていることを止めるように言われています。
  • ゴルーチンは、結果なしで行われたことを報告します。
  • ゴルーチンは処理を完了してシャットダウンしたことを報告します。

これらのルールには例外がありますが、これらは主要なユースケースであり、この記事で焦点を当てるものです。 私はこれらのルールの例外を最初のコードの匂いと考えます。

データなしのシグナリングの利点の一つは、単一のゴルーチンが一度に多くのゴルーチンに信号を送ることができることです。 データによるシグナリングは、常にゴルーチン間の1対1の交換です。

Signaling With Data

データで信号を送る場合、必要な保証のタイプに応じて3つのチャネル設定オプションを選択できます。

図3 : データによるシグナリング

3つのチャンネルオプションはUnbuffered、Buffered>1、またはBuffered=1です。

  • Guarantee

    • バッファされていないチャンネルは、送信された信号が受信されたことを保証します。
      • は、信号の送信が完了する前に信号の受信が行われるためです。
  • 保証なし

    • サイズが>1のバッファリングチャネルは、送信された信号が受信されたことを保証しません。
      • なぜなら、信号の送信は、信号の受信が完了する前に行われるからである。
  • 遅延保証

    • size=1のバッファリングチャネルは遅延保証を提供します。 これは、送信された前の信号が受信されたことを保証することができます。
      • 最初の信号の受信は、2番目の信号の送信が完了する前に発生するためです。

バッファのサイズは決して乱数であってはならず、明確に定義された制約に対して常に計算する必要があります。 計算には無限大はなく、時間であろうと空間であろうと、すべてが明確に定義された制約を持っていなければなりません。

データなしの信号

データなしの信号は、主にキャンセルのために予約されています。 それは、あるゴルーチンが別のゴルーチンに、彼らがやっていることを取り消して移動するように信号を送ることを可能にします。 キャンセルはUnbufferedチャネルとBufferedチャネルの両方を使用して実装できますが、データが送信されないときにBufferedチャネルを使用することはコードの臭いです。

図4 : データなしのシグナリング

組み込み関数closeは、データなしで信号を送るために使用されます。 上記の状態セクションで説明したように、閉じているチャネルで信号を受信することはできます。 実際には、閉じたチャネル上の受信はブロックされず、受信操作は常に戻ります。

ほとんどの場合、標準ライブラリcontextパッケージを使用して、データなしでシグナリングを実装します。 contextパッケージは、シグナリングのためにバッファなしのチャネルを使用し、組み込み関数closeはデータなしで信号を送信します。

コンテキストパッケージではなく、キャンセルに独自のチャネルを使用する場合、チャネルのタイプはchan struct{}にする必要があります。 これは、信号のみに使用されるチャネルを示すためのゼロスペース、慣用的な方法です。

シナリオ

これらの属性を設定して、実際にどのように動作するかをさらに理解する最良の方法は、一連のコードシナリオを実行することです。 私は、チャネルベースのコードを読み書きしているときに、goroutinesを人々として考えるのが好きです。 この視覚化は本当に役立ちます、そして私はそれを以下の援助として使用します。

データ保証バッファなしチャンネルを持つ信号

送信されている信号が受信されたことを知る必要がある場合、二つのシナリオが起こります。 これらはタスクの待機と結果の待機です。

シナリオ1-タスクを待つ

マネージャーであり、新しい従業員を雇うことを考えてください。 このシナリオでは、新しい従業員がタスクを実行する必要がありますが、準備が整うまで待機する必要があります。 あなたは彼らが開始する前に、それらに一枚の紙を渡す必要があるためです。

5
https://play.golang.org/p/BnVEHRCcdh

01 func waitForTask() {02 ch := make(chan string)0304 go func() {05 p := <-ch0607 // Employee performs work here.0809 // Employee is done and free to go.10 }()1112 time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)1314 ch <- "paper"15 }

リスト5の02行目では、stringデータが信号とともに送信されるという属性を持つバッファなしのチャネルが作成されます。 その後、ライン04で、従業員が雇われ、自分の仕事をする前にライン05であなたの信号を待つように言われています。 行05は受信チャネルであり、送信する紙を待っている間に従業員がブロックされます。 ペーパーが従業員によって受け取られれば、従業員は仕事を行い、次に行われ、行って自由である。

あなたはマネージャーとして、あなたの新しい従業員と同時に働いています。 そうライン04の従業員を雇った後、従業員にブロックを解除し、信号を送るためにする必要があるものをするあなた自身を(ライン12で)見つける。 注意してください、それはあなたが送信する必要があるこの紙を準備するのにどれくらいの時間がかかるかは不明でした。

最終的にあなたは従業員に信号を送る準備ができています。 14行目では、データを使用して信号を実行し、データはその紙片です。 バッファされていないチャネルが使用されているため、送信操作が完了すると、従業員が論文を受け取ったことが保証されます。 受信は送信の前に行われます。

技術的には、チャネル送信操作が完了するまでに従業員が論文を持っていることがわかっています。 両方のチャネル操作の後、スケジューラは任意の文を実行することを選択できます。 あなたまたは従業員によって実行されるコードの次の行は、非決定的です。 これは、print文を使用すると、物事の順序についてあなたを欺くことができることを意味します。

シナリオ2-結果を待つ

この次のシナリオでは、物事が逆になります。 今回は、あなたの新しい従業員は、彼らが雇われているときにすぐにタスクを実行したい、とあなたは自分の仕事の結果を待つ必要があります。 あなたが続けることができる前に、あなたがそれらからの紙を必要とするので、あなたは待つ必要があります。

6
https://play.golang.org/p/VFAWHxIQTP

01 func waitForResult() {02 ch := make(chan string)0304 go func() {05 time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)0607 ch <- "paper"0809 // Employee is done and free to go.10 }()1112 p := <-ch13 }

リスト6の02行目では、stringデータが信号とともに送信されるという属性を持つバッファなしのチャネルが作成されます。 その後、ライン04で、従業員が雇われ、すぐに仕事に置かれます。 ライン04の従業員を雇った後、ペーパーレポートを待っているライン12のあなた自身を次に見つける。

行05の従業員が作業を完了すると、データを含むチャネル送信を実行することにより、行07の結果が送信されます。 これはバッファなしのチャネルであるため、受信は送信の前に行われ、従業員は結果を受信したことが保証されます。 従業員がこの保証を持っていれば、彼らは行われ、自由に行くことができます。 このシナリオでは、あなたはそれがタスクを完了するために従業員を取るために起こっているどのくらいのアイデアを持っていません。

コスト/ベネフィット

バッファなしチャネルは、送信される信号が受信されたことを保証します。 これは素晴らしいですが、何も無料ではありません。 この保証のコストは不明なレイテンシです。 タスクの待機シナリオでは、従業員はあなたがその論文を送るのにどれくらいの時間がかかるのか分かりません。 結果の待機シナリオでは、従業員がその結果を送信するのにどれくらいの時間がかかるかわかりません。

どちらのシナリオでも、この未知のレイテンシは保証が必要であるため、私たちが生きなければならないものです。 この保証された動作がなければ、ロジックは機能しません。

データ付き信号-保証なし-バッファ付きチャネル>1

送信されている信号が受信されたことを知る必要がない場合は、次の2つのシナリオが有効になります:ファンアウトとドロップ。

バッファリングされたチャネルには、送信されるデータを格納するために使用できる明確に定義されたスペースがあります。 そうどの位スペースを必要とするかいかに決定するか。 これらの質問に答えてください:

  • 私は完了するべき仕事の明確に定義された量を持っていますか?
    • どのくらいの仕事がありますか?
  • 私の従業員が追いつくことができない場合、私は新しい仕事を捨てることができますか?
    • どのくらいの優れた仕事が私を容量に入れていますか?
  • プログラムが予期せず終了した場合、どのレベルのリスクを受け入れることができますか?
    • バッファ内で待機しているものはすべて失われます。

これらの質問がモデリングしている動作に意味がない場合は、1より大きいバッファリングされたチャネルを使用することはおそらく間違ってい

シナリオ1-ファンアウト

ファンアウトパターンを使用すると、同時に働く問題で明確に定義された数の従業員をスローすることができます。 あなたはすべてのタスクのために一人の従業員を持っているので、あなたが受け取るどのように多くのレポートを正確に知っています。 これらのレポートをすべて受信するために、ボックスに適切なスペースがあることを確認できます。 これはあなたの従業員があなたが彼らのレポートを提出するのを待つ必要がないという利点があります。 しかし、彼らは同じ時間またはその近くのボックスに到着した場合、それぞれがあなたのボックスにレポートを配置するターンを取る必要があります。

あなたが再びマネージャーであると想像してみてくださいが、今回は従業員のチームを雇います。 各従業員に実行させたい個々のタスクがあります。 個々の従業員が仕事を終えると、彼らはあなたの机の上にあなたの箱に置かなければならない紙のレポートをあなたに提供する必要があります。

7
https://play.golang.org/p/8HIt2sabs_

01 func fanOut() {02 emps := 2003 ch := make(chan string, emps)0405 for e := 0; e < emps; e++ {06 go func() {07 time.Sleep(time.Duration(rand.Intn(200)) * time.Millisecond)08 ch <- "paper"09 }()10 }1112 for emps > 0 {13 p := <-ch14 fmt.Println(p)15 emps--16 }17 }

リスト7の03行目では、stringデータが信号とともに送信される属性を持つバッファリング-チャネルが作成されます。 今回は、02行目で宣言されたemps変数のおかげで、20個のバッファでチャネルが作成されます。

05行目から10行目までの間に20人の従業員が雇用され、すぐに仕事に就きます。 あなたは、各従業員がライン07を取るために起こっているどのくらいのアイデアを持っていません。 その後、行08で、従業員は紙のレポートを送信しますが、今回は送信が受信を待機するのをブロックしません。 各従業員のためのボックス内のスペースがあるので、チャネル上の送信は、同じ時間またはその近くにレポートを送信することができ、他の従業員と競合しています。

12行目から16行目の間のコードはあなただけです。 これは、20人の従業員全員が仕事を終えてレポートを送信するのを待つ場所です。 12行目では、あなたはループにあり、13行目では、あなたのレポートを待っているチャネル受信でブロックされています。 レポートが受信されると、レポートは14行目に印刷され、従業員が完了したことを示すためにローカルカウンタ変数が減少します。

シナリオ2-ドロップ

ドロップパターンを使用すると、従業員が能力を持っているときに仕事を捨てることができます。 これはあなたの顧客からの仕事を受け入れ続け、その仕事の受諾の背圧か潜伏を決して適用しないことの利点がある。 ここでの鍵は、あなたが本当に能力を持っているときに知っているので、あなたがやろうとする仕事の量にコミットしたりコミットしたりしない 通常、統合テストまたはメトリックは、この番号を識別するために必要なものです。

あなたが再びマネージャーであり、仕事を成し遂げるために単一の従業員を雇うと想像してください。 従業員に実行させたい個々のタスクがあります。 従業員が彼らの仕事を終えると同時に彼らがされることを知ることを気にしない。 重要なのは、新しい仕事を箱に入れることができるかどうかです。 送信を実行できない場合は、ボックスがいっぱいで、従業員が定員に達していることがわかります。 この時点で、物事が動き続けることができるように、新しい仕事を破棄する必要があります。

8
https://play.golang.org/p/PhFUN5itiv

01 func selectDrop() {02 const cap = 503 ch := make(chan string, cap)0405 go func() {06 for p := range ch {07 fmt.Println("employee : received :", p)08 }09 }()1011 const work = 2012 for w := 0; w < work; w++ {13 select {14 case ch <- "paper":15 fmt.Println("manager : send ack")16 default:17 fmt.Println("manager : drop")18 }19 }2021 close(ch)22 }

リスト8の03行目では、stringデータが信号とともに送信されるという属性を持つバッファリング-チャネルが作成されます。 今回は、02行目で宣言されたcap定数のおかげで、5つのバッファでチャネルが作成されます。

05行目から09行目までの間には、作業を処理するために単一の従業員が雇われています。 チャネル受信にはfor rangeが使用されます。 一枚の紙が受信されるたびに、それはライン07で処理されます。

11行目から19行目までの間に、従業員に20枚の紙を送ろうとします。 今回は、selectステートメントを使用して、14行目の最初のcase内で送信を実行します。 16行目のselect内でdefault句が使用されているため、バッファに余裕がなくなったために送信がブロックされると、17行目を実行すると送信が放棄されます。

最後に、21行目で、組み込み関数closeがチャネルに対して呼び出されます。 これは、彼らが行われ、彼らは彼らの割り当てられた仕事を完了した後に行くこと自由である従業員にデータなしで信号を送ります。.

コスト/ベネフィット

バッファリングされたチャネルが1より大きい場合、送信される信号が受信される保証はありません。 この保証から離れることの利点は、2つのゴルーチン間の通信の遅延が減少するか、またはまったくないことです。 ファンアウトシナリオでは、レポートを送信する各従業員のためのバッファスペースがあります。 ドロップシナリオでは、バッファは容量のために測定され、容量に達した場合、物事が動き続けることができるように作業が削除されます。

どちらのオプションでも、この保証の欠如は、レイテンシの削減がより重要であるため、私たちが生きなければならないものです。 ゼロから最小のレイテンシの要件は、システム全体のロジックに問題を提起しません。

Signal With Data-Delayed Guarantee-Buffered Channel1

新しい信号を送信する前に送信された前の信号が受信されたかどうかを知る必要がある場合は、タスクの待機シナリオが発生します。

シナリオ1-タスクを待つ

このシナリオでは、新しい従業員がいますが、彼らはただ一つのタスク以上のことをするつもりです。 あなたは彼らに多くのタスク、次々に供給しようとしています。 ただし、新しいタスクを開始する前に、個々のタスクを完了する必要があります。 彼らは一度に一つのタスクでのみ動作することができますので、作業のハンドオフの間に待ち時間の問題がある可能性があります。 従業員が次のタスクに取り組んでいるという保証を失うことなく遅延を減らすことができれば、それが役立ちます。

これは、バッファリングされたチャネルが1の利点を有するところである。 あなたと従業員の間で予想されるペースですべてが実行されている場合、どちらも他の人を待つ必要はありません。 あなたが一枚の紙を送るたびに、バッファは空です。 あなたの従業員がより多くの仕事のために達する度に、緩衝は完全である。 それは作業の流れの完全な対称です。

最高の部分はこれです。 いつでも一枚の紙を送信しようとすると、バッファがいっぱいであるためにできない場合は、従業員が問題を抱えていることがわかり、停止します。 これはその遅れた保証が入って来るところである。 バッファが空で、送信を実行すると、従業員が送信した最後の作業を確実に実行したことが保証されます。 送信を実行してもできない場合は、送信できない保証があります。

リスト9
https://play.golang.org/p/4pcuKCcAK3

01 func waitForTasks() {02 ch := make(chan string, 1)0304 go func() {05 for p := range ch {06 fmt.Println("employee : working :", p)07 }08 }()0910 const work = 1011 for w := 0; w < work; w++ {12 ch <- "paper"13 }1415 close(ch)16 }

リスト9の02行目では、stringデータが信号とともに送信されるという属性を持つサイズ1のバッファリング-チャネルが作成されます。 04行目から08行目までの間には、作業を処理するために単一の従業員が雇われています。 チャネル受信にはfor rangeが使用されます。 一枚の紙が受信されるたびに、それはライン06で処理されます。

10行目から13行目の間で、タスクを従業員に送信し始めます。 従業員が送信できるだけ速く実行できる場合、2人の間の待ち時間が短縮されます。 しかし、送信するたびに正常に実行されるため、送信した最後の作業が作業されていることが保証されます。

最後に15行目で、組み込み関数closeがチャネルに対して呼び出されます。 これは、彼らが行われ、行くために自由である従業員にデータなしで信号を送ります。 ただし、あなたが提出した最後の作品はfor rangeが終了する前に受信(フラッシュ)されます。

Signal Without Data-Context

この最後のシナリオでは、contextパッケージのContext値を使用して実行中のgoroutineをキャンセルする方法がわかります。 これはすべて、データなしで信号を実行するために閉じられているバッファなしのチャネルを利用することによって機能します。

あなたは最後にマネージャーであり、仕事を成し遂げるために一人の従業員を雇います。 この時間は、従業員が終了するための時間のいくつかの未知の量を待って喜んでではありません。 あなたは、個別の期限にあり、従業員が時間内に終了しない場合、あなたは待って喜んでではありません。

10
https://play.golang.org/p/6GQbN5Z7vC

01 func withTimeout() {02 duration := 50 * time.Millisecond0304 ctx, cancel := context.WithTimeout(context.Background(), duration)05 defer cancel()0607 ch := make(chan string, 1)0809 go func() {10 time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)11 ch <- "paper"12 }()1314 select {15 case p := <-ch:16 fmt.Println("work complete", p)1718 case <-ctx.Done():19 fmt.Println("moving on")20 }21 }

リスト10の02行目では、従業員がタスクを完了するまでの時間を表すduration値が宣言されています。 この値は、タイムアウトが50ミリ秒のcontext.Context値を作成するために04行目で使用されます。 contextパッケージのWithTimeout関数は、Context値とキャンセル関数を返します。

contextパッケージは、持続時間が満たされるとContext値に関連付けられたバッファなしチャネルを閉じるgoroutineを作成します。 あなたは、物事がどのようになるかにかかわらず、cancel関数を呼び出す責任があります。 これにより、Context用に作成されたものがクリーンアップされます。 cancel関数が複数回呼び出されても問題ありません。

05行目では、cancel関数は、この関数が終了すると実行されるように遅延されます。 これは、従業員が自分の仕事の結果を送信するために使用されます。 その後、行09から12で、従業員が雇われ、すぐに仕事に置かれます。 あなたはそれが完了するために従業員を取るために起こっているどのくらいのアイデアを持っていません。

14行目から20行目の間では、select文を使用して2つのチャネルで受信します。 15行目に受信すると、従業員が結果を送信するのを待ちます。 18行目の受信では、contextパッケージが50ミリ秒が経過していることを通知するかどうかを確認します。 最初に受信した信号は、処理された信号になります。

このアルゴリズムの重要な側面は、1のバッファリングされたチャネルの使用です。 従業員が時間内に終了しない場合は、従業員に通知を与えることなく移動しています。 従業員の観点から、彼らは常にあなたに11行目のレポートを送信し、あなたがそこにいるか、それを受け取ることができない場合、彼らは盲目です。 バッファなしのチャネルを使用すると、従業員は移動した場合にレポートを送信しようとすると永遠にブロックされます。 これにより、goroutineリークが発生します。 そのため、これを防ぐためにバッファリングされたチャネル1が使用されています。

結論

保証、チャネル状態、および送信に関するシグナリングの属性は、チャネル(または並行性)を使用するときに知って理解することが重要です。 彼らはあなたが書いている並行プログラムとアルゴリズムのために必要な最良の動作を実装する際にあなたを導くのに役立ちます。 彼らはあなたがバグを見つけ、潜在的に悪いコードを嗅ぐのに役立ちます。

この記事では、さまざまなシナリオでシグナリングの属性がどのように機能するかを示すいくつかのサンプルプログラムを共有しました。 すべてのルールには例外がありますが、これらのパターンは開始するのに適した基盤です。

これらの概要を、チャネルについていつ、どのように効果的に考え、使用するかの概要として確認してください:

言語力学

  • チャンネルを使用して、ゴルーチンを調整および調整します。
    • データの共有ではなく、シグナリング属性に焦点を当てます。
    • は、共有状態へのアクセスを同期させるための使用に疑問を呈しています。
      • このためにチャンネルが簡単にできる場合がありますが、最初は質問します。
  • バッファなしチャンネル:
    • 受信は送信の前に発生します。
    • 利点:信号が受信されたことを100%保証します。
    • : 信号が受信されるときの不明なレイテンシ。
  • バッファリングされたチャネル:
    • 送信は受信の前に発生します。
    • 利点:シグナリング間のブロッキング遅延を低減します。
    • コスト:信号が受信されたときの保証はありません。
      • バッファが大きいほど、保証は少なくなります。
      • バッファの1つは、保証の遅延送信を与えることができます。
  • チャネルを閉じる:
    • Closeは受信の前に行われます(バッファリングされたように)。
    • キャンセルや期限の通知に最適です。
  • nilチャネル:
    • 送受信ブロック。
    • シグナリングをオフにする
    • レート制限や短期間の停止に最適です。

設計思想

  • チャネル上の任意の送信により、送信ゴルーチンがブロックされる可能性がある場合:
    • 1より大きいバッファリングチャネルを使用することは許可されていません。
      • 1より大きいバッファには理由/測定値が必要です。
    • 送信goroutineがブロックしたときに何が起こるかを知っている必要があります。
  • チャネル上の任意の送信が送信ゴルーチンをブロックさせない場合:
    • 各送信のバッファの正確な数があります。
      • ファンアウトパターン
    • あなたは最大容量のために測定されたバッファを持っています。
      • ドロップパターン
  • より少しは緩衝との多くである。
    • バッファについて考えるとき、パフォーマンスについて考えないでください。
    • バッファは、シグナリング間のブロッキング遅延を減らすのに役立ちます。
      • ブロッキング遅延をゼロにすることは、必ずしもスループットを向上させるとは限りません。
      • バッファが十分なスループットを与えている場合は、それを保持してください。
      • 質問バッファが一つ以上であり、サイズを測定します。
      • 十分なスループットを提供する可能な最小のバッファを見つけます。

コメントを残す

メールアドレスが公開されることはありません。