ゴルーチン、チャンネル

行くの言語は、私たちは似たスレッドが、軽いことを知って、ゴルーチンと呼ばれる概念があります。
次のプログラムは、我々は、直列2つのループ機能を実行するには:

package main

import "fmt"

func main() {
    loop()
    loop()
}

func loop() {
    for i := 0; i < 10; i++ {
        fmt.Printf("%d ", i)
    }
}

確かに、出力は以下のようになります。

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 

私たちはキーワードがゴルーチン、複数の実行を定義し、開始するために行く使用することができ、ゴルーチンが走った上でのループを入れてみましょう:

0 1 2 3 4 5 6 7 8 9 
//或有可能是下面这样
0 1 2 3 4 5 6 7 8 9 0 1 2 
//亦或是下面这样
0 1 2 3 4 5 6 7 8 9 0 

私たちは、繰り返し上記の結果はこのようになりますでしょう上記のコードを実行しているが、2回だけ出力0-9を完了できませんでした、明らかに我々はまた、ああトリップしゴルーチンを開いて、旅のメインラインを走りました。

これは、主な機能が引き出されたゴルーチンループで実行するための十分な時間、いわゆることが判明し、「髪(子スレッド)ヤンが添付されることができるものを肌(メインスレッド)、。」

主な機能は、あまりにも早く終了し、私たちは、主な方法は、それを待つことです抜ける途中でそれを停止する方法を見つける必要があります:

package main

import (
    "fmt"
    "time"
)

func main() {
    go loop() //启动一个goroutine
    loop()
    time.Sleep(time.Second) //停顿一秒
}

func loop() {
    for i := 0; i < 10; i++ {
        fmt.Printf("%d ", i)
    }
}

この出力は、目的が達成された、2件のツアーを行います。
ゴルーチンの終了時に、次のメインラインは「ねえ、私は上で実行します!」アプローチのメインラインを遮断する、いわゆるのように、私たちは、Pythonはリコールの下で終了してすべてのスレッドを待つと言う場合でも、待って採用されたアプローチは、良いではありません言葉遣い:

for thread in threads:
    thread.join()

はい、私たちはメインラインを遮断生きるために同じようなことに参加する必要があります。すなわち、チャネルです

信道

どのようなチャネルでありますか?簡単に言えば、それは互いに通信ゴルーチン間のものです。私たちのUnixで同様の配管は、(プロセス間でメッセージを渡すことができます)、およびメッセージングゴルーチン間でメッセージを受信します。実際には、それは、ゴルーチン間で共有メモリを行うには、です。
確立するためのチャネルを使用してください:

var channel chan int = make(chan int)

そのメッセージはHexiang新道路預金やニュースを得るようですか?例:

package main

import "fmt"

func main() {
    var messages = make(chan string)
    go func(message string) {
        messages <- message //存消息
    }("Ping!")

    fmt.Println(<-messages) //取消息
}

デフォルトでは、メッセージストアチャネルと取るのメッセージがブロックされている(交通渋滞の問題を言って、バッファリングされていないチャネルと呼ばれ、それ以降のバッファの概念を理解するために)。

つまり、バッファなしチャネルメッセージとかかった時間は、保留中のメッセージが保存される現在、もう一方の端の準備ができていない限り、ゴルーチンで。
例えば、以下の主な機能と関数foo:

package main

var ch = make(chan int)

func foo() {
    ch <- 0 //向ch中加数据,如果没有其他goroutine来取走这个数据,那么挂起foo, 直到main函数把0这个数据拿走
}
func main() {
    go foo()
    <-ch //从ch取数据,如果ch中还没放数据,那就挂起main线,直到foo函数中放数据为止
}

「ゴルーチン」の部分に戻って質問に、その後、現在のゴルーチンをブロックすることができ、チャネルは、メインラインを伝えるためにチャネルを使用して問題「私はメインを終えどのように伝えるゴルーチン」に遭遇するので:

package main

import "fmt"

var ch = make(chan int)

func loop() {
    for i := 0; i < 10; i++ {
        fmt.Printf("%d ", i)
    }
    ch <- 0 //执行完毕了,发个消息
}

func main() {

    go loop()
    <- ch //main在此阻塞住,直到线程跑完, 取到消息.
}

あなたは、メインチャンネルをブロックするために持っていない場合、それはメインラインを終了するのは時期尚早だろう、ループラインを実行する機会を持っていません

実際には、バッファリングされていないチャネルは、データを格納していないデータの流れのための唯一の責任があるん、なぜあなたがそれを言うのですか?

  • 非バッファチャネルからデータをフェッチ、データ・ストリームは、彼らが来ることができる前に、または現在の行詰まりしなければなりません
  • データがバッファリングされていないチャンネルを流れ、他のゴルーチンは、このデータを取らないための場合は、現在の行詰まり

だから、あなたがテストすることができます次は、どのような場合には、我々はバッファリングされていないチャンネルの大きさが0で測定された(len(channel))
チャネルは、データフローであるならば、我々はまた、データ、またはチャネルの乾燥を追加し、我々が取るためにデータがない利用可能なチャンネルに流入してきましたデータはありますか?これは、デッドロックにつながるだろう
デッドロックデッドロックの例を:

package main

func main() {
    ch := make(chan int)

    <-ch //阻塞main goroutine,信道ch被锁
}

このプログラムの実施は、このエラーを報告します表示されます。

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
    /Users/XXX/Go/src/main.go:6 +0x4d

Process finished with exit code 2

デッドロックとは何ですか?オペレーティングシステムは、について話している、すべてのスレッドまたはプロセスがリソースの解放を待っています。プログラム、一つだけゴルーチン、あなたは内部またはそれに格納されたデータにデータを追加するとき、チャネル、およびブロック現在のゴルーチンをロックと同じように、つまり、ゴルーチンの全ては、(実際には、メインライン上の1)は、(オープンチャネルを待っています誰もが少しデッドロックである、)データチャネルが開いていない取りませんでした。
:私は、デッドロックがここにデッドロックの数例で、非常に興味深いトピックであることがわかった
1つのゴルーチンでのみバッファなしチャネル動作、必ずデッドロックすること。たとえば、あなただけのメイン機能でチャンネルがあります:

package main

import "fmt"

func main() {
    ch := make(chan int)

    ch <- 1 //1流入信道,堵塞当前线, 没人取走数据信道不会打开
    fmt.Println("This line code won`t run") //在此行执行之前Go就会报死锁
}

2.次は、デッドロックの例です。

package main

import "fmt"

var ch1 = make(chan int)
var ch2 = make(chan int)

func say(s string) {
    fmt.Println(s)
    ch1 <- <- ch2 //ch1等待ch2流出的数据
}

func main() {
    go say("hello")
    <-ch1 //堵塞主线
}

メインデータと流出CH1、CH2のCH1データような流出物中のような、しかしデータを待っCH2が流れ、これは、二つのゴルーチン、すなわちデッドロックされています。

3.実際には、概要ビュー、なぜデッドロックがでしょうか?バッファなし流入路全く流れの中や外なし流れ、それがデッドロックにつながるいない場合に発生します。ゴルーチン移動全部または非緩衝チャンネルで活性化は、データラインに格納する必要があり、データラインはジョブペアに運ばれたことが分かります。したがって、たとえば以下の特定のデッドロック:

package main

func main() {
    c, quit := make(chan int), make(chan int)

    go func() {
        c <- 1 //c通道的数据没有被其他goroutine读取走,堵塞当前goroutine
        quit <- 0 //quit始终没有办法写入数据
    }()
    
    <- quit //quit等待数据的写
}

慎重な分析、それが原因にある:など、主要流出チャンネルデータを辞め待つ待機を書いて終了し、Cのfuncがチャネルをブロックし、すべてのゴルーチン、デッドロックされています。
ビューの単純な、2つのラインの合計は、FUNCラインデータがに流入及びCは確かに、メインラインにデッドロックチャネルを流れません。

すべてが本当にデータチャネルへのアクセスのために当てはまらない場合は、デッドロックはありますか?

次のようにカウンターの例は次のとおりです。

package main

func main() {

    c := make(chan int)

    go func() {
        c <- 1
    }()
}

プログラムが正常に終了し、非常に単純な、それは私たちの要約は動作しませんではないか、理由は非常に恥ずかしい人の、主な待ち時間は、自分自身の開始、他のゴルーチンではなかったと終了するので、データはチャンネルC、総実行に流入されていませんゴルーチン、および発生し、デッドロックエラーがない何の閉塞はありません。

そして、デッドロックのソリューション?
最も単純には、データの削除は削除されていない、データがデータを運ぶことができない無バッファチャンネル以来、投入されていないに、その後すぐに取ります!
具体的には、それはとてもデッドロックを回避するために、実施例3でのデッドロック状態であります:

package main

func main() {
    c, quit := make(chan int), make(chan int)

    go func() {
        c <- 1 //c通道的数据没有被其他goroutine读取走,堵塞当前goroutine
        quit <- 0 //quit始终没有办法写入数据
    }()
    
    <- c    //取走c的数据
    <- quit //quit等待数据的写
}

別の解決策は、チャンネルをバッファリングされている、すなわち、Cは、データバッファサイズが設けられています。

c := make(chan int, 1)

この場合、Cは、データキャッシュすることができます。つまり、cは、現在の行を中断最初のデータのみを一定時間の容量にブロックされている他のゴルーチンを削除するまで、ライン電流がハングアップします追加し、行っていないデータに、と言うことです容量がブロックされていません。

我々はすでに知っているシーケンスの中と外のUnbufferedチャネルデータは、バッファなしチャネルは、流入必見流出缶を、データを保存されることはありません。
以下の手順を守ってください。

package main

import "fmt"

var ch chan int = make(chan int)

func foo(id int) {
    ch <- id
}
func main() {

    //开启5个routine
    for i := 0; i < 5; i++ {
        go foo(i)
    }

    //取出信道中的数据
    for i := 0; i < 5; i++ {
        fmt.Print(<- ch)
    }
}

彼らは5つのゴルーチンを開設し、その後、順番にデータを取ります。実際には、細分化の全体の実装プロセスは、その後、データ5行が順次チャネルch、メインプリント流れ、我々はそれを参照マクロは非緩衝チャンネルデータは、まず、第1、出てくるされているが、バッファリングされていないチャンネルが記憶されていませんデータは、データの流れのための唯一の責任があります

バッファ付きチャンネル

そして最後のトピックに、実際には、英語でより多くのキャッシュ・チャンネルは、技術を話す:バッファリングチャンネル。

ワードバッファの意味は、バッファが唯一のチャネルデータを流れることができる、データもキャッシュすることができます。それは能力である、データ・ワードが格納されている、あなたがチャンネルにハングアップすることができ、電流を遮断することが取り除かデータラインを待つ必要はありません。

バッファチャンネルが満杯状態に達し、その後、もはやそれ以上のデータを運ばないので、彼らは、ブロックされて表示され、ときに「あなたはデータを取るために持っている、データは」に流れ込むことができます

チャンネルを宣言し、我々は2番目のパラメータはその能力を示すために作る(デフォルトは0、つまり、バッファなしです):

var ch chan int = make(chan int, 2) // 写入2个元素都不会阻塞当前goroutine, 存储个数达到2的时候会阻塞

次の例では、バッファは、バッファリングされていないチャネルch流入三つの要素であってもよいです。

package main

func main() {
    ch := make(chan int, 3)
    ch <- 1
    ch <- 2
    ch <- 3   
}

あなたは、データに流入しようとすると、チャネルchは、メインラインを詰まらせ、デッドロックを報告しました。
換言すれば、バッファチャンネルはフル稼働にするときにロックされます。

実際には、チャネルは、FIFOバッファであり、我々はスレッドセーフとしてキューバッファのチャンネルを見ることができます:

package main

import "fmt"

func main() {

    ch := make(chan int, 3)
    ch <- 1
    ch <- 2
    ch <- 3

    fmt.Println(<-ch) //1
    fmt.Println(<-ch) //2
    fmt.Println(<-ch) //3
}

あなたを読んチャネルデータとチャネル近い距離を行く私たちは、チャネルを読むために言語を使用することができ、一つ一つのチャネルを読むために上記のコードは、あまりにも多くが気であることを見つけることがあります。

package main

import "fmt"

func main() {

    ch := make(chan int, 3)
    ch <- 1
    ch <- 2
    ch <- 3

    for v := range ch {
        fmt.Println(v)
    }
}

あなたは上記のコードを実行した場合、チャネルは、読み取りの終わりでなくなるまで範囲が閉じていないので、デッドロックエラーが、報告されます。すなわち、バッファチャネルがアップ乾燥場合、範囲は、現在のゴルーチンをブロックするので、わずかにデッドロック、です。

だから、我々はそれが読み取りを終了したときにリードチャネルが空であることを考えるのは比較的簡単で、このような状況を避けるようにしてください:

package main

import "fmt"

func main() {

    ch := make(chan int, 3)
    ch <- 1
    ch <- 2
    ch <- 3

    for v := range ch {
        fmt.Println(v)
        if len(ch) <= 0 { //如果现在数据量为0,跳出循环
            break
        }
    }
}

上記の方法は正常に出力することができ、我々は唯一のデータCHを保持するための検査チャネルサイズ法は、この例では、発生しているチャネル・アクセス時にすべてのデータを削除するために使用することができない、それは今や外側に取るものであることに留意されたいです、チャネルの大きさが減少しています。

もう一つの方法は、明示的にチャネルを閉じることです。

package main

import "fmt"

func main() {

    ch := make(chan int, 3)
    ch <- 1
    ch <- 2
    ch <- 3

    //显式地关闭信道
    close(ch)

    for v := range ch {
        fmt.Println(v)
    }
}

チャネルが閉鎖されるデータフローを無効にし、読み取り専用です。我々はまだ閉じられたチャネルからデータを取得することができますが、データを書き込むことはできません。

まあ、我々はすべてのゴルーチン仕上げを開くのを待って、チャンネル詰まっメインラインを使用して、元の質問にあるgorountineプログラムを待ちます。

これは、小さなゴルーチンの多くのアウトモデルであり、彼らはそれぞれ、自分の走ったメインラインに最終報告書を走りました。

私たちは、プログラムの以下の2つのバージョンを議論しました:

  1. バッファリングされていない唯一の単一のメインチャネル輻輳
  2. バッファチャネル番号の容量ゴルーチン

オプション1の場合は、コードサンプルは、おそらく次のようになります。

package main

import "fmt"

var quit chan int //只开一个信道

func foo(id int) {
    fmt.Println(id)
    quit <- 0 //ok,finished
}

func main() {

    count := 1000
    quit = make(chan int) //无缓冲

    for i := 0; i < count; i++ {
        go foo(i)
    }

    for i := 0; i < count; i++ {
        <- quit
    }
}

スキーム2については、1000年チャンネルに:

quit = make(chan int, count) // 容量1000

実際には、唯一の違いは、非バッファ、バッファがあるということです。
このシナリオでは、タスクを完了することができます2、可能です。

  • バッファなしチャネルデータは1のグループである「内と外の流れ。」
  • チャネル・バッファはメモリであり、その後、一緒に流出します

おすすめ

転載: www.cnblogs.com/itbsl/p/11456432.html