サーチ…
前書き
チャネルには、指定された型の値が含まれます。値はチャネルに書き込まれ、そこから読み取られ、先入れ先出しでチャネル内を循環します。いくつかのメッセージを含むことができるバッファリングされたチャネルとバッファされていないチャネルは区別できません。チャネルは通常、ゴルーチン間の通信に使用されますが、他の状況でも役立ちます。
構文
- make(chan int)//バッファされていないチャンネルを作成する
- make(chan int、5)//容量が5のバッファードチャネルを作成する
- close(ch)//チャンネル "ch"を閉じます。
- ch < - 1 //チャネル "ch"に1の値を書き込み、
- val:= <-ch //チャンネル "ch"から値を読み込む
- val、ok:= <-ch //別の構文。 okはチャンネルが閉じているかどうかを示すブールです
備考
空のstruct make(chan struct{})
保持しているチャネルは、チャネル上に情報が送信されておらず、同期のために純粋に使用されているという明確なメッセージです。
バッファされていないチャネルに関しては、チャネル書き込みは、対応する読み出しが別のゴルーチンから発生するまでブロックされます。同じことは、ライターを待っている間のチャネルリードブロッキングにも当てはまります。
範囲を使用する
チャネルから複数の値を読み取る場合、 range
を使用するのが一般的なパターンです。
func foo() chan int {
ch := make(chan int)
go func() {
ch <- 1
ch <- 2
ch <- 3
close(ch)
}()
return ch
}
func main() {
for n := range foo() {
fmt.Println(n)
}
fmt.Println("channel is now closed")
}
出力
1
2
3
channel is now closed
タイムアウト
チャネルは、タイムアウトを実装するためにしばしば使用されます。
func main() {
// Create a buffered channel to prevent a goroutine leak. The buffer
// ensures that the goroutine below can eventually terminate, even if
// the timeout is met. Without the buffer, the send on the channel
// blocks forever, waiting for a read that will never happen, and the
// goroutine is leaked.
ch := make(chan struct{}, 1)
go func() {
time.Sleep(10 * time.Second)
ch <- struct{}{}
}()
select {
case <-ch:
// Work completed before timeout.
case <-time.After(1 * time.Second):
// Work was not completed after 1 second.
}
}
コーディネートゴルーチン
メインスレッドが各ステップ間でいくつかの作業を行う必要がある、2段階のプロセスでゴルーチンを想像してみましょう。
func main() {
ch := make(chan struct{})
go func() {
// Wait for main thread's signal to begin step one
<-ch
// Perform work
time.Sleep(1 * time.Second)
// Signal to main thread that step one has completed
ch <- struct{}{}
// Wait for main thread's signal to begin step two
<-ch
// Perform work
time.Sleep(1 * time.Second)
// Signal to main thread that work has completed
ch <- struct{}{}
}()
// Notify goroutine that step one can begin
ch <- struct{}{}
// Wait for notification from goroutine that step one has completed
<-ch
// Perform some work before we notify
// the goroutine that step two can begin
time.Sleep(1 * time.Second)
// Notify goroutine that step two can begin
ch <- struct{}{}
// Wait for notification from goroutine that step two has completed
<-ch
}
バッファリングされていないものとバッファされていないもの
func bufferedUnbufferedExample(buffered bool) {
// We'll declare the channel, and we'll make it buffered or
// unbuffered depending on the parameter `buffered` passed
// to this function.
var ch chan int
if buffered {
ch = make(chan int, 3)
} else {
ch = make(chan int)
}
// We'll start a goroutine, which will emulate a webserver
// receiving tasks to do every 25ms.
go func() {
for i := 0; i < 7; i++ {
// If the channel is buffered, then while there's an empty
// "slot" in the channel, sending to it will not be a
// blocking operation. If the channel is full, however, we'll
// have to wait until a "slot" frees up.
// If the channel is unbuffered, sending will block until
// there's a receiver ready to take the value. This is great
// for goroutine synchronization, not so much for queueing
// tasks for instance in a webserver, as the request will
// hang until the worker is ready to take our task.
fmt.Println(">", "Sending", i, "...")
ch <- i
fmt.Println(">", i, "sent!")
time.Sleep(25 * time.Millisecond)
}
// We'll close the channel, so that the range over channel
// below can terminate.
close(ch)
}()
for i := range ch {
// For each task sent on the channel, we would perform some
// task. In this case, we will assume the job is to
// "sleep 100ms".
fmt.Println("<", i, "received, performing 100ms job")
time.Sleep(100 * time.Millisecond)
fmt.Println("<", i, "job done")
}
}
チャンネルのブロックとブロック解除
デフォルトでは、channelscの通信は同期です。あなたが価値を送るとき、受信機がなければなりません。それ以外の場合、 fatal error: all goroutines are asleep - deadlock!
次のように:
package main
import "fmt"
func main() {
msg := make(chan string)
msg <- "Hey There"
go func() {
fmt.Println(<-msg)
}()
}
そこにソリューションの使用があります:バッファされたチャネルを使用する:
package main
import "fmt"
import "time"
func main() {
msg :=make(chan string, 1)
msg <- "Hey There!"
go func() {
fmt.Println(<-msg)
}()
time.Sleep(time.Second * 1)
}
仕事が終わるのを待っている
チャネルを使用する一般的な手法は、チャネルから読み込むワーカー(またはコンシューマー)をいくつか作成することです。 sync.WaitGroupを使用すると、それらのワーカーが実行を完了するのを待つ簡単な方法です。
package main
import (
"fmt"
"sync"
"time"
)
func main() {
numPiecesOfWork := 20
numWorkers := 5
workCh := make(chan int)
wg := &sync.WaitGroup{}
// Start workers
wg.Add(numWorkers)
for i := 0; i < numWorkers; i++ {
go worker(workCh, wg)
}
// Send work
for i := 0; i < numPiecesOfWork; i++ {
work := i % 10 // invent some work
workCh <- work
}
// Tell workers that no more work is coming
close(workCh)
// Wait for workers to finish
wg.Wait()
fmt.Println("done")
}
func worker(workCh <-chan int, wg *sync.WaitGroup) {
defer wg.Done() // will call wg.Done() right before returning
for work := range workCh { // will wait for work until workCh is closed
doWork(work)
}
}
func doWork(work int) {
time.Sleep(time.Duration(work) * time.Millisecond)
fmt.Println("slept for", work, "milliseconds")
}