チャネルは
単に機能を同時に実行するだけでは意味がありません。関数の同時実行の意味を反映するために、関数間でデータを交換する必要があります。
共有メモリはデータ交換に使用できますが、共有メモリはさまざまなゴルーチンで競合状態になる傾向があります。データ交換の正確性を確保するために、ミューテックスを使用してメモリをロックする必要があります。これにより、パフォーマンスの問題が発生します。
Go言語の並行性モデルはCSP(Communicating Sequential Processes)であり、共有メモリではなく共有メモリを介した通信を提唱しています。
goroutineがGoプログラムの同時実行本体である場合、channelはそれらの間の接続です。チャネルは、あるゴルーチンが特定の値を別のゴルーチンに送信できるようにする通信メカニズムです。
Go言語のチャンネルは特別なタイプです。チャネルはコンベヤーベルトまたはキューのようなものであり、データの送受信の順序を確実にするために常に先入れ先出し(先入れ先出し)ルールに従います。各チャネルは特定のタイプのコンジットです。つまり、チャネルを宣言するときに、その要素タイプを指定する必要があります。
チャネルタイプ
チャネルは、タイプ、参照タイプです。チャネルタイプを宣言するための形式は次のとおりです。
チャネルの作成
チャネルは参照型であり、チャネル型のnull値はnilです。
package main
import "fmt"
func main() {
//var 变量 chan 元素类型
var ch1 chan int
fmt.Printf("%T,%v\n", ch1, ch1)
ch2 := make(chan int, 3)
fmt.Printf("%T,%v\n", ch2, ch2)
}
チャネル操作
チャネルには、送信、受信、およびクローズの3つの操作があります。
送信と受信の両方で<-記号を使用します。
package main
import "fmt"
func main() {
ch := make(chan int, 1)
//发送
ch <- 10
// 接收
i, ok := <-ch
if !ok {
fmt.Printf("%v", ok)
return
}
fmt.Printf("%v", i)
// 关闭
close(ch)
}
// 对一个关闭的通道再发送值就会导致panic。
// 对一个关闭的通道进行接收会一直获取值直到通道为空。
// 对一个关闭的并且没有值的通道执行接收操作会得到对应类型的零值。
// 关闭一个已经关闭的通道会导致panic。
バッファリングされていないチャネル
import "fmt"
func main() {
ch := make(chan int)
ch <- 10
c := <-ch
fmt.Println(c)
}
エラーを報告する
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
D:/go/src/liqunkeji.com/dlq/main.go:7 +0x65
exit status 2
デッドロックエラーが発生するのはなぜですか?
ch:= make(chan int)を使用してバッファなしチャネルを作成するため、バッファなしチャネルは、誰かが値を受信した場合にのみ値を送信できます。コミュニティにエクスプレスキャビネットと収集ポイントがないのと同じように、宅配業者は電話をかけるときにアイテムを配達する必要があります。簡単に言えば、バッファリングされていないチャネルを受信してから送信する必要があります。
上記のコードは、コードch <-10の行をブロックしてデッドロックを形成します。この問題を解決するにはどうすればよいですか?
package main
import (
"fmt"
"sync"
)
var wy sync.WaitGroup
func chanan(c chan int) {
defer wy.Done()
ch := <-c
fmt.Println(ch)
}
func main() {
wy.Add(1)
ch := make(chan int)
go chanan(ch)
ch <- 10
wy.Wait()
}
バッファリングされていないチャネルでの送信操作は、別のゴルーチンがチャネルで受信操作を実行するまでブロックされます。その後、値は正常に送信され、2つのゴルーチンは実行を継続します。逆に、受信操作が最初に実行されると、受信者のゴルーチンは、別のゴルーチンがチャネルで値を送信するまでブロックされます。
バッファリングされていないチャネルを通信に使用すると、送信側と受信側のゴルーチンが同期されます。したがって、バッファリングされていないチャネルは同期チャネルとも呼ばれます。
バッファ
リングされたチャネル上記の問題を解決する別の方法は、バッファリングされたチャネルを使用することです。make関数を使用してチャネルを初期化するときに、チャネルの容量を指定できます。次に例を示します。
package main
import "fmt"
func main() {
ch := make(chan int, 1)
ch <- 10
c := <-ch
fmt.Println(c)
}
チャネル容量がゼロより大きい限り、チャネルはバッファリングされたチャネルであり、チャネル容量はチャネルに格納できる要素の数を表します。コミュニティのエクスプレスキャビネットにグリッドが非常に多いのと同じように、グリッドがいっぱいになると、グリッドが収まらず、ブロックされます。他の誰かが宅配便を利用する場合は、中に入れることができます。
組み込みのlen関数を使用してチャネル内の要素の数を取得し、cap関数を使用してチャネルの容量を取得できますが、これを行うことはめったにありません。
範囲の場合、チャネルから値を周期的に取得します
package main
import "fmt"
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go func() {
for i := 0; i <= 99; i++ {
ch1 <- i
}
close(ch1)
}()
go func() {
for {
v, ok := <-ch1
if !ok {
break
}
ch2 <- v * v
}
close(ch2)
}()
for i := range ch2 {
fmt.Println(i)
}
}
上記の例から、値を受信したときにチャネルが閉じているかどうかを判断する方法は2つあることがわかりますが、通常はforrangeメソッドを使用します。範囲に使用してチャネルをトラバースし、チャネルが閉じているときに範囲を終了します。
一方向チャネル
複数のタスク関数間でパラメータとしてチャネルを渡すことがあります。多くの場合、関数でチャネルを送信のみまたは受信のみに制限するなど、さまざまなタスク関数でチャネルを使用して制限します。
Go言語は、この状況に対処するための一方向のチャネルを提供します。たとえば、上記の例を次のように変更します。
package main
import "fmt"
func count(out chan<- int) {
for i := 0; i < 100; i++ {
out <- i
}
close(out)
}
func squar(out chan<- int, in <-chan int) {
for i := range in {
out <- i * i
}
close(out)
}
func prin(out <-chan int) {
for i := range out {
fmt.Println(i)
}
}
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go count(ch1)
go squar(ch2, ch1)
prin(ch2)
}
その中で、
chan <-intは書き込み専用の一方向チャネル(int型の値のみを書き込むことができます)であり、送信はできますが受信はできません;
<-chan intは読み取り専用の一方向チャネル(のみからint型の値を読み取ります)、受信操作は実行できますが、送信操作は実行できません。
関数パラメータの転送および任意の割り当て操作では、双方向チャネルを単方向チャネルに変換できますが、その逆はできません。
チャネルの概要
//チャネルnilは空ではありませんが、フルはフルではありません
//受信ブロッキング受信値ブロッキング受信値受信値
//ブロッキング送信値を送信送信値ブロッキング送信値
//パニックを閉じます。データを読み取った後はゼロ値を返します。データを読み取った後はゼロ値を返します。データを読み取った後はゼロ値を返します。データを正常に読み取った後はゼロ値を返します。
。