この記事では、Go 言語の Goroutine とチャネルについて予備的に理解することができます。
Go 言語の CSP
同時実行モデルの実装には、2 つの主要コンポーネントが含まれています。1 つは Goroutine
で、もう 1 つはチャンネル
。この記事ではその基本的な使い方と注意点を紹介します。
Goroutine
は、Go
アプリケーションの基本的な実行ユニットであり、軽量のユーザーレベルのスレッドです。 、最下層は coroutine
(コルーチン) によって実現される同時実行です。ご存知のとおり、コルーチンはユーザー モードで実行されるユーザー スレッドであるため、Go
プログラムの実行中に Goroutine
もスケジュールされます。
構文: go function/methodgo キーワード function/method を通じて ## を作成できます#ゴルーチン
。 コード例:
import ( "fmt" "time" ) func printGo() { fmt.Println("具名函数") } type G struct { } func (g G) g() { fmt.Println("方法") } func main() { // 基于具名函数创建 goroutine go printGo() // 基于方法创建 goroutine g := G{} go g.g() // 基于匿名函数创建 goroutine go func() { fmt.Println("匿名函数") }() // 基于闭包创建 goroutine i := 0 go func() { i++ fmt.Println("闭包") }() time.Sleep(time.Second) // 避免 main goroutine 结束后,其创建的 goroutine 来不及运行,因此在此休眠 1 秒 }
実行結果:
闭包 具名函数 方法 匿名函数
複数の
Goroutine が存在する場合、実行順序は固定されません。したがって、印刷するたびに結果が異なります。 コードからわかるように、
キーワードを使用して、名前付き関数
/ に基づいて groutin を作成できます。 Method、Goroutine
も 匿名関数 /closures に基づいて作成できます。 それでは、
はどのようにして終了するのでしょうか?通常の状況では、Goroutine
関数の実行が終了するか、実行が戻る限り、それは Goroutine
の終了を意味します。 Goroutine
の関数またはメソッドに戻り値がある場合、Goroutine
が終了すると無視されます。
は、Go 同時実行モデルにおいて重要な役割を果たします。これは、Goroutine
間の通信を実装するために使用でき、また、Goroutine
間の同期を実装するためにも使用できます。
は複合データ型のため、宣言時に channel 内の要素を指定する必要があります。 ### タイプ。
宣言構文: var ch chan string
である上記のコードを通じて、要素タイプが
string
channel を宣言します。
string 型の要素のみを保存できます。
channel は参照型であり、データを書き込むために初期化する必要があります。
make によって初期化されます。
import ( "fmt" ) func main() { var ch chan string ch = make(chan string, 1) // 打印 chan 的地址 fmt.Println(ch) // 向 ch 发送 "Go" 数据 ch <- "Go" // 从 ch 中接收数据 s := <-ch fmt.Println(s) // Go }
ch <- xxx を通じて、x := < を介して channel
変数 ch
にデータを送信できます。 ;- ch データは
channel 変数
ch から受信できます。
バッファ付きチャネルとバッファなしチャネル
: <pre class="brush:js;toolbar:false;">ch := make(chan string)</pre>
バッファリングされていない channel
の送信操作と受信操作は同期しています。送信操作が実行されると、対応する
がブロックされます。受信操作を実行する別の Goroutine
があり、その逆も同様です。送信操作と実行操作を同じゴルーチンの下に置くとどうなるでしょうか?次のコードを見てください: <pre class="brush:js;toolbar:false;">import (
"fmt"
)
func main() {
ch := make(chan int)
// 发送数据
ch <- 1 // fatal error: all goroutines are asleep - deadlock!
// 接收数据
n := <-ch
fmt.Println(n)
}</pre>
プログラムを実行すると、ch <-
で
が発生し、すべての Goroutine にプロンプトが表示されます。
休止状態では、デッドロック状態になります。この状況を回避するには、channel
の送受信操作を別の Goroutine
で実行する必要があります。 <pre class="brush:js;toolbar:false;">import (
"fmt"
)
func main() {
ch := make(chan int)
go func() {
// 发送数据
ch <- 1
}()
// 接收数据
n := <-ch
fmt.Println(n) // 1
}</pre>
上記の例から結論付けることができます: バッファリングされていない channel
の送信および受信操作は 2 つの異なる
で実行する必要があります。そうしないと、発生しますデッドロック
画像。 容量が指定されている場合、バッファ付き
channel
ch := make(chan string, 5)
バッファ付き channel
およびバッファなし
は異なります。送信操作を実行するとき、channel
のバッファーがいっぱいでない限り、バッファーがいっぱいになるまで Goroutine
はハングしません。送信操作を実行すると、Goroutine
がハングします。コード例: <pre class="brush:js;toolbar:false;">func main() {
ch := make(chan int, 1)
// 发送数据
ch <- 1
ch <- 2 // fatal error: all goroutines are asleep - deadlock!
}</pre>
両方とも送信できるチャネルの送信専用タイプと受信専用タイプを宣言します
ch := make(chan int, 1)
channel
ch := make(<-chan int, 1)
channel
ch := make(chan<- int, 1)
通常只发送 channel
类型和只接收 channel
类型,会被用作函数的参数类型或返回值:
func send(ch chan<- int) { ch <- 1 } func recv(ch <-chan int) { <-ch }
通过内置函 close(c chan<- Type)
,可以对 channel
进行关闭。
在发送端关闭 channel
在 channel
关闭之后,将不能对 channel
执行发送操作,否则会发生 panic
,提示 channel
已关闭。
func main() { ch := make(chan int, 5) ch <- 1 close(ch) ch <- 2 // panic: send on closed channel }
管道 channel
之后,依旧可以对 channel
执行接收操作,如果存在缓冲区的情况下,将会读取缓冲区的数据,如果缓冲区为空,则获取到的值为 channel
对应类型的零值。
import "fmt" func main() { ch := make(chan int, 5) ch <- 1 close(ch) fmt.Println(<-ch) // 1 n, ok := <-ch fmt.Println(n) // 0 fmt.Println(ok) // false }
如果通过 for-range 遍历 channel
时,中途关闭 channel
则会导致 for-range
循环结束。
本文首先介绍了 Goroutine
的创建方式以及其退出的时机是什么。
其次介绍了如何创建 channel
类型变量的有缓冲与无缓冲的创建方式。需要注意的是,无缓冲的 channel
发送与接收操作,需要在两个不同的 Goroutine
中执行,否则会发送 error
。
接下来介绍如何定义只发送和只接收的 channel
类型。通常只发送 channel
类型和只接收 channel
类型,会被用作函数的参数类型或返回值。
最后介绍了如何关闭 channel
,以及关闭之后的一些注意事项。
以上がGo 言語での Goroutine とチャネルの予備調査の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。