Go channelはソース分析を実現します.
13363 ワード
go通路はgoの合併スケジュールに基づいて実現します.自分は複雑ではありません.go同時スケジュールは私のこの文章を見てください.go同時スケジュール原理は勉強します.
1.hannelデータ構造
2.チャンネルを作成して実現する
チャンネルを作成する例:
ch:=make(chan int,4)
実現関数:
func make chan(t*change type,size int 64)*hchan
大まかな実現:
上の行のコードを実行するとnewのhchan構造ができます.同時にdataqsiz=4のintタイプの循環列を作成します.つまり、4つの要素が入っている行列です.順番に中にデータを書きます.書いたら0から書きます.この順序の索引はhchan.sendxです.
3.データの送信
送信データの例:
ch
送信データ実現関数:
func chans(c*hchan、ep unsafe.Pointer、block book、calerpc uintptr)book l
ステップは、データを送信する先頭アドレスを指します.
1:受信待ち行列は空ではなく、受信待ち行列から最初の受信者*susudogを取り出し、データをsudog.elemにコピーし、コピー関数はmemmove用のアセンブリで実現し、受信者データをあなたに通知しました.受信側の協働は待ち状態から実行可能状態に変更され、現在の協働手順を協働キューに参加し、スケジュールを待つことになります.
2:受取者がいなくて、バッファエリアがあり、満杯ではなく、直接にバッファにデータをコピーし、書き込みバッファエリアの位置はhchan.buf[sendx+]であり、バッファエリアがsendx=0に達したら、循環行列の実現であり、sendx指定の位置にデータを書き込み、hchan.qcount++
3:受信者がいない、バッファエリアがない、またはいっぱいであれば、現在のプロセスに対応するPのsudogキューからstruct sudogを取って、sudog.elemにデータをコピーして、sudogをsendqキューに参加して、受信者に通知します.現在のプロセスがブロックされ、呼び覚まされるのを待っています.受信者が通知を受けたら、引き続き実行します.受信データが完了したら送信者に通知します.送信側協働状態が待ち状態から運転可能状態に変更され、協働に参加して運転可能な列が実行されるのを待っています.
1:チャネルバッファが満杯になる前に、送りたいデータをバッファにコピーするだけで戻ってきます.
2:受信者がいる場合、受信者のデータ構造にコピーされたデータがある(最終的にデータを受信した変数ではなく、受信関数を実行したときに最終的にデータを受信した変数にコピーされる).受信協働を起動すると詰まります.もちろん、バッファがいっぱいで、受信者もいない場合は、データを送信キューにパッケージ化します.現在のプロセスは待機状態に設定されています.この状態はスケジュールされず、受信者がデータを受信したときに起動されます.
4.受信データ
受信データの例:
val:=
受信データ実現関数:
1.送信キューが空ではない(バッファがいっぱいであることを説明します).送信キューから最初の送信者*sudogを取り出します.
1.1.バッファエリアがなく、送信待ちのデータをそのままsudog.elemにコピーし、受信データの変数valに保存し、送信者に処理が完了したことを通知します.引き続き実行してもいいです.
1.2バッファエリアがあり、バッファエリアhchan.buf[recvx]に対応する要素をvalにコピーし、送信者sudog.elemをhchan.buf[recvx]にコピーし、送信者は順番に書き、通常のFIFOは先進先に出ることを保証するために、先にコピーして、列の先頭要素を対応するバッファにコピーします.バッファがいっぱいになったら、キューを書きます.受信する時はバッファからデータを取って、外した後に空いている位置を送信キューから一番目に取って埋めて、対応するGを呼び覚ましてください.送信列が空いていない限り、バッファは必ず満たされます.
2.送信キューが空で、バッファエリアが空ではないので、バッファエリアのhchan.buf[recvx]に対応する要素をval,hchan.qcountにコピーします.
3.送信キューが空で、バッファエリアも空です.受信待ちのデータがないと、受信プロセスはsudogに包装され、受信待ち行列recvqに参加して、現在実行中のフローが詰まります.データを送信したら呼び覚まされます.
5.チャンネルFIFOは一回説明しています.
5.1:バッファがいっぱいではありません.データを送るとバッファに入ります.データを受け取るとバッファが出ます.分かりやすいです.
5.2:バッファがいっぱいになりました.送信データは待ち行列に入ります.受信データは先にバッファキューを出します.つまり受信したいデータのために、列が出るのを待って、データをバッファキューが列から出たばかりの位置に存在します.列が出たばかりの位置はバッファキューの末尾に相当します.つまり待ち行列の先頭がバッファキューの末尾につながっています.待ち行列の列の先頭をキャッシュ列の列の最後に参加して、バッファキューがいっぱいであることを保証して、減らすのはバッファキューの中のデータで、先進的な先に出ることを保証します.
5.3:データの受信、バッファまたは待ち行列にデータがあります.最初のものを持っていくと、待ち行列がバッファの最後に接続されていることを保証します.つまり、バッファの最後に空きがあると、待ち行列が列から出てきて、バッファの最後に塗り込めます.そうでないと、自分で梱包して受信キューに参加します.前Gが待機状態に入ると、データの送信があれば自然に通知します.
まとめ:Go channel goの同時スケジュールに基づいて、渋滞と非閉塞の二つの通信方式を実現する.
<div id=「right-1」class=「col-lg-12 col-sm-4 col-xs-4 ad」
<div id=「right-2」class=「col-lg-12 col-sm-4 col-xs-4 ad」
<div id=「right-3」class=「col-lg-12 col-sm-4 col-xs-4 ad」
1.hannelデータ構造
type hchan struct {
qcount uint //
dataqsiz uint //
buf unsafe.Pointer //
elemsize uint16 //
closed uint32 // ,0 ,1
elemtype *_type //
sendx uint //
recvx uint //
recvq waitq //
sendq waitq //
lock mutex //
}
type waitq struct {
first *sudog
last *sudog
}
2.チャンネルを作成して実現する
チャンネルを作成する例:
ch:=make(chan int,4)
実現関数:
func make chan(t*change type,size int 64)*hchan
大まかな実現:
上の行のコードを実行するとnewのhchan構造ができます.同時にdataqsiz=4のintタイプの循環列を作成します.つまり、4つの要素が入っている行列です.順番に中にデータを書きます.書いたら0から書きます.この順序の索引はhchan.sendxです.
3.データの送信
送信データの例:
ch
送信データ実現関数:
func chans(c*hchan、ep unsafe.Pointer、block book、calerpc uintptr)book l
ステップは、データを送信する先頭アドレスを指します.
func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {
lock(&c.lock)
if c.closed != 0 {
unlock(&c.lock)
panic(plainError("send on closed channel"))
}
if sg := c.recvq.dequeue(); sg != nil {
//
// , , ,
// , , ,
send(c, sg, ep, func() { unlock(&c.lock) }, 3)
return true
}
if c.qcount < c.dataqsiz {
// , , ,
qp := chanbuf(c, c.sendx)
typedmemmove(c.elemtype, qp, ep)
c.sendx++
if c.sendx == c.dataqsiz {
c.sendx = 0
}
c.qcount++
unlock(&c.lock)
return true
}
if !block {
unlock(&c.lock)
return false
}
// ,ch //
// , , , G ,
gp := getg()
mysg := acquireSudog()
c.sendq.enqueue(mysg)
goparkunlock(&c.lock, "chan send", traceEvGoBlockSend, 3)
// G , ,
releaseSudog(mysg)
return true
}
大まかな実現:1:受信待ち行列は空ではなく、受信待ち行列から最初の受信者*susudogを取り出し、データをsudog.elemにコピーし、コピー関数はmemmove用のアセンブリで実現し、受信者データをあなたに通知しました.受信側の協働は待ち状態から実行可能状態に変更され、現在の協働手順を協働キューに参加し、スケジュールを待つことになります.
2:受取者がいなくて、バッファエリアがあり、満杯ではなく、直接にバッファにデータをコピーし、書き込みバッファエリアの位置はhchan.buf[sendx+]であり、バッファエリアがsendx=0に達したら、循環行列の実現であり、sendx指定の位置にデータを書き込み、hchan.qcount++
3:受信者がいない、バッファエリアがない、またはいっぱいであれば、現在のプロセスに対応するPのsudogキューからstruct sudogを取って、sudog.elemにデータをコピーして、sudogをsendqキューに参加して、受信者に通知します.現在のプロセスがブロックされ、呼び覚まされるのを待っています.受信者が通知を受けたら、引き続き実行します.受信データが完了したら送信者に通知します.送信側協働状態が待ち状態から運転可能状態に変更され、協働に参加して運転可能な列が実行されるのを待っています.
1:チャネルバッファが満杯になる前に、送りたいデータをバッファにコピーするだけで戻ってきます.
2:受信者がいる場合、受信者のデータ構造にコピーされたデータがある(最終的にデータを受信した変数ではなく、受信関数を実行したときに最終的にデータを受信した変数にコピーされる).受信協働を起動すると詰まります.もちろん、バッファがいっぱいで、受信者もいない場合は、データを送信キューにパッケージ化します.現在のプロセスは待機状態に設定されています.この状態はスケジュールされず、受信者がデータを受信したときに起動されます.
4.受信データ
受信データの例:
val:=
受信データ実現関数:
func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool)
func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) {
lock(&c.lock)
if sg := c.sendq.dequeue(); sg != nil {
// Found a waiting sender. If buffer is size 0, receive value
// directly from sender. Otherwise, receive from head of queue
// and add sender's value to the tail of the queue (both map to
// the same buffer slot because the queue is full).
recv(c, sg, ep, func() { unlock(&c.lock) }, 3)
return true, true
}
if c.qcount > 0 {
// Receive directly from queue
qp := chanbuf(c, c.recvx)
if ep != nil {
typedmemmove(c.elemtype, ep, qp)
}
typedmemclr(c.elemtype, qp)
c.recvx++
if c.recvx == c.dataqsiz {
c.recvx = 0
}
c.qcount--
unlock(&c.lock)
return true, true
}
if !block {
unlock(&c.lock)
return false, false
}
//
//
gp := getg()
mysg := acquireSudog()
c.recvq.enqueue(mysg)
// G ,
goparkunlock(&c.lock, "chan receive", traceEvGoBlockRecv, 3)
// G
mysg.c = nil
releaseSudog(mysg)
return true, !closed
}
大まかな実現:1.送信キューが空ではない(バッファがいっぱいであることを説明します).送信キューから最初の送信者*sudogを取り出します.
1.1.バッファエリアがなく、送信待ちのデータをそのままsudog.elemにコピーし、受信データの変数valに保存し、送信者に処理が完了したことを通知します.引き続き実行してもいいです.
1.2バッファエリアがあり、バッファエリアhchan.buf[recvx]に対応する要素をvalにコピーし、送信者sudog.elemをhchan.buf[recvx]にコピーし、送信者は順番に書き、通常のFIFOは先進先に出ることを保証するために、先にコピーして、列の先頭要素を対応するバッファにコピーします.バッファがいっぱいになったら、キューを書きます.受信する時はバッファからデータを取って、外した後に空いている位置を送信キューから一番目に取って埋めて、対応するGを呼び覚ましてください.送信列が空いていない限り、バッファは必ず満たされます.
2.送信キューが空で、バッファエリアが空ではないので、バッファエリアのhchan.buf[recvx]に対応する要素をval,hchan.qcountにコピーします.
3.送信キューが空で、バッファエリアも空です.受信待ちのデータがないと、受信プロセスはsudogに包装され、受信待ち行列recvqに参加して、現在実行中のフローが詰まります.データを送信したら呼び覚まされます.
5.チャンネルFIFOは一回説明しています.
5.1:バッファがいっぱいではありません.データを送るとバッファに入ります.データを受け取るとバッファが出ます.分かりやすいです.
5.2:バッファがいっぱいになりました.送信データは待ち行列に入ります.受信データは先にバッファキューを出します.つまり受信したいデータのために、列が出るのを待って、データをバッファキューが列から出たばかりの位置に存在します.列が出たばかりの位置はバッファキューの末尾に相当します.つまり待ち行列の先頭がバッファキューの末尾につながっています.待ち行列の列の先頭をキャッシュ列の列の最後に参加して、バッファキューがいっぱいであることを保証して、減らすのはバッファキューの中のデータで、先進的な先に出ることを保証します.
5.3:データの受信、バッファまたは待ち行列にデータがあります.最初のものを持っていくと、待ち行列がバッファの最後に接続されていることを保証します.つまり、バッファの最後に空きがあると、待ち行列が列から出てきて、バッファの最後に塗り込めます.そうでないと、自分で梱包して受信キューに参加します.前Gが待機状態に入ると、データの送信があれば自然に通知します.
まとめ:Go channel goの同時スケジュールに基づいて、渋滞と非閉塞の二つの通信方式を実現する.
<div id=「right-1」class=「col-lg-12 col-sm-4 col-xs-4 ad」
<div id=「right-2」class=「col-lg-12 col-sm-4 col-xs-4 ad」
<div id=「right-3」class=「col-lg-12 col-sm-4 col-xs-4 ad」