Leapcell:最適なGolangホスティング用サーバレスプラットフォーム
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
メソッドを通じてクローズできます。複数のgoroutineは追加の同期対策を考えることなく、チャネルからデータを receive
したり、チャネルにデータを send
したりすることができます。Channelは先入先出(FIFO)キューとして機能し、データの受信と送信の順序は一貫しています。
Channelの受信は複数値の代入をサポート
v, ok := <-ch
この方法を使用して、Channelがクローズされたかどうかをチェックできます。
send文
send文はChannelにデータを送信するために使用され、例えば ch <- 3
のようになります。その定義は以下の通りです:
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
は生成されたゼロ値であり、チャネルがクローズされているか空であることを示します。
ブロッキング
デフォルトでは、送信と受信は相手が準備できるまでブロックされます。この方法を使用すると、明示的なロックや条件変数を使用せずに、goroutine内で同期を行うことができます。例えば、公式の例:
import "fmt"
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // sumを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 // cから受信
fmt.Println(x, y, x+y)
}
上記のコードでは、x, y := <-c, <-c
という文は計算結果がチャネルに送信されるのを待ち続けます。
バッファ付きチャネル
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は疑似的にランダムに1つの 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
}
}
タイムアウト
select
の1つの重要な応用はタイムアウト処理です。処理する case
がない場合、select
文はブロックされるため、このときタイムアウト操作が必要な場合があります。例えば:
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
が出力されます。ここでは time.After
メソッドを使用しています。このメソッドは型 <-chan Time
の片方向チャネルを返します。指定された時間に、現在の時刻が返されるチャネルに送信されます。
TimerとTicker
- Timer:これは1つの将来のイベントを表すタイマーです。待機時間を指定でき、チャネルを提供します。指定された時間に、チャネルに時刻値が提供されます。例えば:
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:これは定期的にトリガーされるタイマーです。一定期間(間隔)でイベント(現在の時刻)をチャネルに送信します。チャネルの受信者は、一定の時間間隔でチャネルからイベントを読み取ることができます。例えば:
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
を使うと、チャネルの状態を確認し、値がゼロ値なのか通常に読み取った値なのかを判断できます:
c := make(chan int, 10)
close(c)
i, ok := <-c
fmt.Printf("%d, %t", i, ok) //0, false
同期
チャネルはゴルーチン間の同期に使用できます。例えば、次の例ではメインゴルーチンが done
チャネルを通じてワーカーがタスクを完了するのを待ちます。ワーカーはタスクを完了した後、チャネルにデータを送信することでメインゴルーチンにタスクの完了を通知することができます:
import (
"fmt"
"time"
)
func worker(done chan bool) {
time.Sleep(time.Second)
// タスクが完了したことを通知
done <- true
}
func main() {
done := make(chan bool, 1)
go worker(done)
// タスクが完了するのを待つ
<-done
}
Leapcell:最適なGolangホスティング用サーバレスプラットフォーム
最後に、Golangのデプロイに最適なプラットフォームをおすすめします:Leapcell
1. 多言語対応
- JavaScript、Python、Go、またはRustで開発できます。
2. 無制限のプロジェクトを無料でデプロイ
- 使用量に応じてのみ課金 — リクエストがなければ料金はかかりません。
3. 圧倒的なコスト効率
- 使い放題でアイドル料金はかかりません。
- 例:25ドルで平均応答時間60msで694万回のリクエストをサポートできます。
4. 合理化された開発者体験
- 直感的なUIによる簡単なセットアップ。
- 完全自動化されたCI/CDパイプラインとGitOps統合。
- アクション可能なインサイトを得るためのリアルタイムメトリックとロギング。
5. 簡単なスケーラビリティと高パフォーマンス
- 高い並行性を簡単に処理するための自動スケーリング。
- オペレーションオーバーヘッドゼロ — 構築に集中できます。
LeapcellのTwitter:https://x.com/LeapcellHQ