Golang Channelをマスター:ゼロからヒーローへ
Lukas Schneider
DevOps Engineer · Leapcell

ChannelはGo言語のコアな型です。これは、並行コアユニットが通信を実現するためにデータを送受信できるパイプラインと見なすことができます。その演算子は矢印 <-
です。
Channel操作の例
ch <- v
: 値v
をChannelch
に送信します。v := <-ch
: Channelch
からデータを受信し、そのデータをv
に割り当てます。(矢印の方向はデータの流れの方向を示します。)
Channelの作成と使用
map
や slice
のようなデータ型と同様に、Channelは使用する前に作成する必要があります。
ch := make(chan int)
Channelの型
Channel型の定義形式は次のとおりです。
ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType.
これには3つの型の定義が含まれており、オプションの <-
はチャネルの方向を表します。方向が指定されていない場合、チャネルは双方向であり、データの送受信が可能です。
chan T
: 型T
のデータを受信および送信できます。chan<- float64
: 型float64
のデータを送信するためにのみ使用できます。<-chan int
: 型int
のデータを受信するためにのみ使用できます。
<-
は常に左端の型と最初に結合します。例:
chan<- chan int
:chan<- (chan int)
と同等です。chan<- <-chan int
:chan<- (<-chan int)
と同等です。<-chan <-chan int
:<-chan (<-chan int)
と同等です。chan (<-chan int)
.
makeによるChannelの初期化と容量の設定
make(chan int, 100)
容量は、Channelが保持できる要素の最大数を表します。つまり、Channelのバッファのサイズです。容量が設定されていないか、0に設定されている場合、Channelにバッファがないことを意味し、送信者と受信者の両方の準備が整った場合にのみ通信が発生します(ブロッキング)。バッファを設定すると、ブロッキングが発生しない場合があります。バッファがいっぱいになった場合にのみ、send
操作がブロックされ、バッファが空の場合、receive
操作がブロックされます。nil
チャネルは通信しません。
Channelのクローズ
Channelは、組み込みの close
メソッドを通じて閉じることができます。複数のゴルーチンは、追加の同期手段を考慮せずに、チャネルからデータを 受信/送信
できます。Channelは、先入れ先出し(FIFO)キューとして機能し、データの受信と送信の順序は一貫しています。
Channelの受信はMultiple - Value Assignmentをサポートします
v, ok := <-ch
この方法を使用すると、Channelが閉じられたかどうかを確認できます。
sendステートメント
sendステートメントは、ch <- 3
のように、データをChannelに送信するために使用されます。その定義は次のとおりです。
SendStmt = Channel "<-" Expression. Channel = Expression.
通信が開始される前に、チャネルと式は両方とも評価される必要があります。例:
c := make(chan int) defer close(c) go func() { c <- 3 + 4 }() i := <-c fmt.Println(i)
上記のコードでは、(3 + 4)
が最初に7に計算され、チャネルに送信されます。通信は、送信が実行されるまでブロックされます。前述のように、バッファリングされていないチャネルの場合、受信者の準備ができた場合にのみ送信操作が実行されます。バッファがあり、バッファがいっぱいでない場合、送信操作は実行されます。閉じられたチャネルにデータの送信を続けると、実行時パニックが発生します。nil
チャネルにデータを送信すると、無期限にブロックされます。
receive演算子
<-ch
は、チャネル ch
からデータを受信するために使用されます。この式は、受信するデータがあるまでブロックされます。nil
チャネルからデータを受信すると、無期限にブロックされます。閉じられたチャネルからデータを受信してもブロックされませんが、すぐに戻ります。送信されたデータを受信すると、要素型のゼロ値を返します。前述のように、チャネルが閉じられているかどうかを確認するために、追加の戻りパラメータを使用できます。
x, ok := <-ch x, ok = <-ch var x, ok = <-ch
OK
が false
の場合、受信した x
は生成されたゼロ値であり、チャネルは閉じられているか空であることを示します。
blocking
デフォルトでは、送信と受信は、相手の準備が整うまでブロックされます。この方法は、明示的なロックまたは条件変数を使用せずに、ゴルーチンで同期するために使用できます。たとえば、公式の例:
import "fmt" func sum(s []int, c chan int) { sum := 0 for _, v := range s { sum += v } c <- sum // send sum to c } func main() { s := []int{7, 2, 8, -9, 4, 0} c := make(chan int) go sum(s[:len(s)/2], c) go sum(s[len(s)/2:], c) x, y := <-c, <-c // receive from c fmt.Println(x, y, x+y) }
上記のコードでは、ステートメント x, y := <-c, <-c
は、計算結果がチャネルに送信されるのを待ち続けます。
Buffered Channels
make
の2番目のパラメータは、バッファのサイズを指定します。
ch := make(chan int, 100)
バッファを使用することにより、ブロッキングを可能な限り回避し、アプリケーションのパフォーマンスを向上させることができます。
Range
for …… range
ステートメントは、チャネルを処理できます。
func main() { go func() { time.Sleep(1 * time.Hour) }() c := make(chan int) go func() { for i := 0; i < 10; i = i + 1 { c <- i } close(c) }() for i := range c { fmt.Println(i) } fmt.Println("Finished") }
range c
によって生成された反復値は、Channelで送信された値です。チャネルが閉じられるまで反復処理を続けます。上記の例では、close(c)
がコメントアウトされている場合、プログラムは for …… range
行でブロックされます。
select
select
ステートメントは、一連の可能な送受信操作を選択して処理するために使用されます。これは switch
に似ていますが、通信操作の処理にのみ使用されます。その case
は、送信ステートメント、受信ステートメント、または default
にすることができます。受信ステートメントは、1つまたは2つの変数に値を割り当てることができ、受信操作である必要があります。最大で1つの default
ケースが許可され、通常はケースリストの最後に配置されます。例:
import "fmt" func fibonacci(c, quit chan int) { x, y := 0, 1 for { select { case c <- x: x, y = y, x+y case <-quit: fmt.Println("quit") return } } } func main() { c := make(chan int) quit := make(chan int) go func() { for i := 0; i < 10; i++ { fmt.Println(<-c) } quit <- 0 }() fibonacci(c, quit) }
複数の case
を同時に処理できる場合、たとえば、複数のチャネルが同時にデータを受信できる場合、Goは擬似ランダムに処理する case
を選択します(擬似ランダム)。処理する必要のある case
がない場合、default
が選択されて処理されます(default case
が存在する場合)。default case
がない場合、select
ステートメントは、処理する必要のある case
ができるまでブロックされます。nil
チャネルでの操作は無期限にブロックされることに注意してください。default case
がない場合、nil
チャネルのみを含む select
は無期限にブロックされます。select
ステートメントは、switch
ステートメントと同様に、ループではなく、処理する1つの case
のみを選択します。チャネルを継続的に処理する場合は、外側に無限 for
ループを追加できます。
for { select { case c <- x: x, y = y, x+y case <-quit: fmt.Println("quit") return } }
timeout
select
の重要なアプリケーションの1つは、タイムアウト処理です。select
ステートメントは、処理の必要な case
がない場合ブロックされるため、この時点でタイムアウト操作が必要になる場合があります。例:
import "time" import "fmt" func main() { c1 := make(chan string, 1) go func() { time.Sleep(time.Second * 2) c1 <- "result 1" }() select { case res := <-c1: fmt.Println(res) case <-time.After(time.Second * 1): fmt.Println("timeout 1") } }
上記の例では、データは2秒後にチャネル c1
に送信されますが、select
は1秒後にタイムアウトするように設定されています。したがって、result 1
ではなく timeout 1
が出力されます。これは、型 <-chan Time
の一方向チャネルを返す time.After
メソッドを使用します。指定された時間に、現在の時間が返されたチャネルに送信されます。
TimerとTicker
- Timer: これは、単一の将来のイベントを表すタイマーです。待機時間を指定でき、Channelを提供します。指定された時間に、Channelは時間値を提供します。例:
timer1 := time.NewTimer(time.Second * 2) <-timer1.C fmt.Println("Timer 1 expired")
上記の2行目は、時間が経過するまで約2秒間ブロックされ、実行を継続します。もちろん、単純に待機したいだけの場合は、time.Sleep
を使用して実現できます。timer.Stop
を使用してタイマーを停止することもできます。
timer2 := time.NewTimer(time.Second) go func() { <-timer2.C fmt.Println("Timer 2 expired") }() stop2 := timer2.Stop() if stop2 { fmt.Println("Timer 2 stopped") }
- Ticker: これは、定期的にトリガーされるタイマーです。イベント(現在の時間)を間隔(interval)でChannelに送信します。Channelの受信者は、固定時間間隔でChannelからイベントを読み取ることができます。例:
ticker := time.NewTicker(time.Millisecond * 500) go func() { for t := range ticker.C { fmt.Println("Tick at", t) } }()
timer
と同様に、ticker
も Stop
メソッドで停止できます。停止すると、受信者はチャネルからデータを受信しなくなります。
close
組み込みの close
メソッドを使用して、チャネルを閉じることができます。チャネルが閉じられた後の送信側と受信側の操作をまとめます。
- チャネル
c
が閉じられている場合、データの送信を続けるとパニックが発生します:send on closed channel
。例:
import "time" func main() { go func() { time.Sleep(time.Hour) }() c := make(chan int, 10) c <- 1 c <- 2 close(c) c <- 3 }
- 閉じられたチャネルから送信されたデータを読み取るだけでなく、ゼロ値を読み取り続けることもできます。
c := make(chan int, 10) c <- 1 c <- 2 close(c) fmt.Println(<-c) //1 fmt.Println(<-c) //2 fmt.Println(<-c) //0 fmt.Println(<-c) //0
range
を介して読み取る場合、チャネルが閉じられた後、for
ループはジャンプアウトします。
c := make(chan int, 10) c <- 1 c <- 2 close(c) for i := range c { fmt.Println(i) }
i, ok := <-c
を使用すると、Channelの状態を表示し、値がゼロ値か通常読み取り値かを判断できます。
c := make(chan int, 10) close(c) i, ok := <-c fmt.Printf("%d, %t", i, ok) //0, false
Synchronization
Channelsは、ゴルーチン間の同期に使用できます。たとえば、次の例では、メインゴルーチンは done
チャネルを介してワーカーがタスクを完了するのを待ちます。ワーカーは、タスクの完了後にチャネルにデータを送信することにより、タスクが完了したことをメインゴルーチンに通知できます。
import ( "fmt" "time" ) func worker(done chan bool) { time.Sleep(time.Second) // Notify that the task is completed done <- true } func main() { done := make(chan bool, 1) go worker(done) // Wait for the task to complete <-done }
Leapcell:Golangホスティングに最適なサーバーレスプラットフォーム
最後に、Golangのデプロイに最適なプラットフォームをお勧めします:Leapcell
1. 多言語サポート
- JavaScript、Python、Go、またはRustで開発します。
2. 無制限のプロジェクトを無料でデプロイ
- 使用量に対してのみ支払い - リクエストなし、料金なし。
3. 比類のないコスト効率
- アイドル料金なしの従量課金制。
- 例:25ドルで平均応答時間60ミリ秒で694万リクエストをサポートします。
4. 合理化された開発者エクスペリエンス
- 簡単なセットアップのための直感的なUI。
- 完全に自動化されたCI / CDパイプラインとGitOpsの統合。
- 実用的な洞察のためのリアルタイムのメトリックとロギング。
5. 簡単なスケーラビリティと高性能
- 高い並行処理を簡単に処理するための自動スケーリング。
- 運用上のオーバーヘッドなし - 構築に集中してください。
Leapcell Twitter:https://x.com/LeapcellHQ