ホームページ > バックエンド開発 > Golang > Go 言語での Goroutine とチャネルの予備調査

Go 言語での Goroutine とチャネルの予備調査

青灯夜游
リリース: 2023-02-02 20:18:01
転載
3268 人が閲覧しました

この記事では、Go 言語の Goroutine とチャネルについて予備的に理解することができます。

Go 言語での Goroutine とチャネルの予備調査

Go 言語の CSP 同時実行モデルの実装には、2 つの主要コンポーネントが含まれています。1 つは Goroutine で、もう 1 つはチャンネル。この記事ではその基本的な使い方と注意点を紹介します。

Goroutine

Goroutine は、Go アプリケーションの基本的な実行ユニットであり、軽量のユーザーレベルのスレッドです。 、最下層は coroutine (コルーチン) によって実現される同時実行です。ご存知のとおり、コルーチンはユーザー モードで実行されるユーザー スレッドであるため、Go プログラムの実行中に Goroutine もスケジュールされます。

#基本的な使い方

構文: go function/method

go キーワード 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

が存在する場合、実行順序は固定されません。したがって、印刷するたびに結果が異なります。 コードからわかるように、

go

キーワードを使用して、名前付き関数 / に基づいて groutin を作成できます。 MethodGoroutine匿名関数 /closures に基づいて作成できます。 それでは、

Goroutine

はどのようにして終了するのでしょうか?通常の状況では、Goroutine 関数の実行が終了するか、実行が戻る限り、それは Goroutine の終了を意味します。 Goroutine の関数またはメソッドに戻り値がある場合、Goroutine が終了すると無視されます。

channel

channel

は、Go 同時実行モデルにおいて重要な役割を果たします。これは、Goroutine 間の通信を実装するために使用でき、また、Goroutine 間の同期を実装するためにも使用できます。

channel の基本操作

#channel

は複合データ型のため、宣言時に 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 から受信できます。 バッファ付きチャネルとバッファなしチャネル

チャネル

の初期化時に容量が指定されていない場合は、バッファなし ##チャネルが作成されます。

: <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">ch := make(chan string)</pre><div class="contentsignin">ログイン後にコピー</div></div>バッファリングされていない channel の送信操作と受信操作は同期しています。送信操作が実行されると、対応する

Goroutine

がブロックされます。受信操作を実行する別の Goroutine があり、その逆も同様です。送信操作と実行操作を同じゴルーチンの下に置くとどうなるでしょうか?次のコードを見てください: <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">import ( &quot;fmt&quot; ) func main() { ch := make(chan int) // 发送数据 ch &lt;- 1 // fatal error: all goroutines are asleep - deadlock! // 接收数据 n := &lt;-ch fmt.Println(n) }</pre><div class="contentsignin">ログイン後にコピー</div></div> プログラムを実行すると、ch <-

致命的エラー

が発生し、すべての Goroutine にプロンプ​​トが表示されます。 休止状態では、デッドロック状態になります。この状況を回避するには、channel の送受信操作を別の Goroutine で実行する必要があります。 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">import ( &quot;fmt&quot; ) func main() { ch := make(chan int) go func() { // 发送数据 ch &lt;- 1 }() // 接收数据 n := &lt;-ch fmt.Println(n) // 1 }</pre><div class="contentsignin">ログイン後にコピー</div></div> 上記の例から結論付けることができます: バッファリングされていない channel の送信および受信操作は 2 つの異なる

Goroutine

で実行する必要があります。そうしないと、発生しますデッドロック画像。 容量が指定されている場合、バッファ付き channel

が作成されます:
ch := make(chan string, 5)
ログイン後にコピー

バッファ付き channel およびバッファなし

chennel

は異なります。送信操作を実行するとき、channel のバッファーがいっぱいでない限り、バッファーがいっぱいになるまで Goroutine はハングしません。送信操作を実行すると、Goroutine がハングします。コード例: <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">func main() { ch := make(chan int, 1) // 发送数据 ch &lt;- 1 ch &lt;- 2 // fatal error: all goroutines are asleep - deadlock! }</pre><div class="contentsignin">ログイン後にコピー</div></div>両方とも送信できるチャネルの送信専用タイプと受信専用タイプを宣言します

channel# and accept ##
ch := make(chan int, 1)
ログイン後にコピー

    channel
  • 変数は上記のコードを通じて取得され、それに対して送受信操作を実行できます。

    channel

    ch := make(<-chan int, 1)
    ログイン後にコピー

  • channel
  • 変数は上記のコードを通じて取得されており、受信のみが可能です。

    送信のみ

    channel

    ch := make(chan<- int, 1)
    ログイン後にコピー

  • channel
  • 変数は上記のコードを通じて取得され、送信のみ可能です。

通常只发送 channel 类型和只接收 channel 类型,会被用作函数的参数类型或返回值:

func send(ch chan<- int) {
   ch <- 1
}

func recv(ch <-chan int) {
   <-ch
}
ログイン後にコピー

channel 的关闭

通过内置函 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视频教程编程教学

    以上がGo 言語での Goroutine とチャネルの予備調査の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:juejin.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート