オリジナルリンク:http://maoqide.live/post/golang/go-concurrency/
ゴー言語の大きな利点の他の言語に比べて、便利で効率的な書き込みを並行コードです。この記事では、特定の同時実行、外出先言語について説明し、並行プログラミング言語のために使用される方法は行きます。
同時実行モデル
- スレッドロック
- 俳優
俳優・モデルは、同時動作のモデルです。「俳優」ベースユニットの同時動作と考えられているプログラムの抽象化である:俳優がメッセージを受信すると、より多くのメッセージの決定を送信し、より多くの俳優を作成し、いくつかの意思決定を行うことができます次のメッセージに答えるためにどのように。
CSP(一連の処理を通信)
同様に一連の処理を通信する、俳優のモデルは、差分のみ(チャネル)を当該メッセージチャネルを送信し、当該CSPメッセージングエンティティではありません。golang同時実行モデルベースのCSP。
推奨図書:「七つの7週間の同時実行モデル。」
ゴルーチンスケジューリング
MPG
- G:ゴルーチン、コルーチン保存された状態、および必要なスタック空間情報実行。
- P:プロセッサ、ゴルーチンキューを保存し、スケジューリングがゴルーチンを行うことができ、あなたは同時ゴルーチンの数を制御することができます。
- M:機械、システムスレッド、ゴルーチンGはP.を通じてMに実行するためにスケジュールされます
次のように図の特定の関係は、次のとおりです。
派遣
- システムコール
Gは、オペレーティング・システム・コールでブロックされている場合は、Gをブロックするだけではなく、また、一緒にスリープ状態に入るG P(エッセンスを奪われるSYSMON)、およびGに接続M溶液を実行します。アイドルがMのこの時点で存在する場合、他のP Gを実行するために継続してその結合;アイドルMが存在しない場合は、まだ実行され、他のGに行かなければならない、それは新しいM.を作成します。 - チャネル/ IO
Gは、Gは、待ち行列に入れ、そして実行可能なM、Gを実行するために、次の試行され、動作チャネル又はネットワークI / O操作でブロックされている場合、Gこの時間が実行可能でない場合操作のためのMは、アンバンドリングP Mを、スリープ状態に入ります。。I / O利用可能なチャネル又は動作が完了すると、キュー内のG待機は、目覚めのRunnableとしてマークされ、待ち行列P内に配置され、M.に結合することが進みます - プリエンプティブスケジューリング
Gプリエンプティブスケジューリングタスクの実行時間の長い(10ミリ秒)に発行されたSYSMON、一旦フラグをつかむGがtrueに設定され、その後、次の呼び出し最初のG関数またはメソッドであるため、ランタイムGがプリエンプトと走行アウトすることができます状態は、ローカルRUNQ Pに、待機時間が予定されています。
SYSMON
プログラムの起動時に行く、ランタイムは10ミリ秒〜一度20USを開始するには、実行しなくても、P mを結合する、(一般的に監視スレッドと呼ばれる)のm個のSYSMONを開始するために呼び出されます、次のタスクを担当します。
- 物理メモリスパンのリリースの5分以上のアイドル。
- 2分以上無い場合は、ガベージコレクション、執行。
- 長期未処理にnetpoll結果タスクキューに追加します。
- Gは、長時間実行されるタスクに発行されたプリエンプティブスケジューリング。
- Pの閉塞を回復するためのシステムコール長い時間。
使い方
ゴルーチン
ゴルーチン単純なモデルを持っている:それは、他のゴルーチン同じアドレス空間で同時に実行される機能です。ゴルーチンは軽量で、スペースの小さな初期割り当ては、動的なヒープメモリの割り当てが必要であること(およびリリース)することができます。
ゴルーチンの使用:
go func(){
// do something
}()
チャネル
上記の例では、非常に単純であるゴルーチン自分の終わり、および実行の結果は、この時点で、我々はチャンネルを必要とする他の機能に送られる信号のレポートを送信しないので、実際のプロジェクトに適用されていません。
チャネル(チャネルバッファへとバッファリングされていない)を初期化します。
ci := make(chan int) // unbuffered channel of integers
cj := make(chan int, 0) // unbuffered channel of integers
cs := make(chan *os.File, 100) // buffered channel of pointers to Files
一般的な使用チャネル:
c := make(chan int) // Allocate a channel.
// Start the sort in a goroutine; when it completes, signal on the channel.
go func() {
list.Sort()
c <- 1 // Send a signal; value does not matter.
}()
doSomethingForAWhile()
<-c // Wait for sort to finish; discard sent value.
c <- 1
チャネル<-
左シンボルは、データを伝送路を示し<- c
、チャネル<-
次いで、変数のデータ・タイプ、またはデータが破棄されるデフォルトを受信する責任左対応があってもよい、右に受信したデータを表します。
データを介して送信されるまで、チャネルの受信端がブロックされます。
受信機は、値を受信するまでバッファリングされていないチャンネルの場合、送信端は、ブロック内のデータを送信することができます。バッファが満杯である場合、受信機が特定の値を受け取るためのバッファチャンネルがある場合、送信者は、バッファがいっぱいになったときにのみブロックされ、待ち。
例1:
var sem = make(chan int, MaxOutstanding)
func handle(r *Request) {
sem <- 1 // Wait for active queue to drain.
process(r) // May take a long time.
<-sem // Done; enable next request to run.
}
func Serve(queue chan *Request) {
for {
req := <-queue
go handle(req) // Don't wait for handle to finish.
}
}
例1は、チャネルバッファは上限があっても、問題があるが、すべてのリクエストに来たが、ゴルーチンサーバーが作成され、制限はありません、理論的には、ゴルーチンランの無限の数があるかもしれないので、サーバーの下の制限を変更する必要があります数のゴルーチン平行。
例2:
func Serve(queue chan *Request) {
for req := range queue {
sem <- 1
go func() {
process(req) // Buggy; see explanation below.
<-sem
}()
}
}
バッファがいっぱいになると、sem <- 1
それがブロックされ、新しいゴルーチンを作成しません。実施例2が、問題もある。golang for
ループは、各サイクルにおけるループ変数が多重化され、req
変数が同じで、最終的にすべての要求につながる、すべてのゴルーチンで共有されます。この問題は、閉鎖することによって解決することができます。
例3:
func Serve(queue chan *Request) {
for req := range queue {
sem <- 1
go func(req *Request) {
process(req)
<-sem
}(req)
}
}
別の解決策のアイデアを、より多くの労働者:
func handle(queue chan *Request) {
for r := range queue {
process(r)
}
}
func Serve(clientRequests chan *Request, quit chan bool) {
// Start handlers
for i := 0; i < MaxOutstanding; i++ {
go handle(clientRequests)
}
<-quit // Wait to be told to exit.
}
チャンネルのチャンネル
チャネル自体1つのネイティブ言語タイプを行っても、チャネルを通過することができます。この機能は、私たちは簡単に便利な機能の多くを達成することができます。
実施例1(非ブロッキング平行フレームRPC):
type Request struct {
args []int
f func([]int) int
resultChan chan int
}
// 调用端
func sum(a []int) (s int) {
for _, v := range a {
s += v
}
return
}
request := &Request{[]int{3, 4, 5}, sum, make(chan int)}
// Send request
clientRequests <- request
// Wait for response.
fmt.Printf("answer: %d\n", <-request.resultChan)
// Server 端
func handle(queue chan *Request) {
for req := range queue {
req.resultChan <- req.f(req.args)
}
}
func Serve(clientRequests chan *Request, quit chan bool) {
// Start handlers
for i := 0; i < MaxOutstanding; i++ {
go handle(clientRequests)
}
<-quit // Wait to be told to exit.
}
実施例2(UNIXパイプの特性、ストリーミング)。
type PipeData struct {
value int
handler func(int) int
next chan int
}
// 流式处理
func handle(queue chan *PipeData) {
for data := range queue {
data.next <- data.handler(data.value)
}
}
pprof
golang建てランタイム/ pprofパッケージは、パフォーマンス分析とモニタリングをコーディングし、これに基づいて、ウェブは、パッケージ限り、このプロジェクトを参照してくださいネット/ HTTP /証明パッケージをカプセル化することができ、Webサービスが自動的に、のパスのデバッグ/ pprofが追加されますあなたは、基本的なパフォーマンス分析を行うことができます。
あなたがメインの機能に直接導入し、組み込みネット/ HTTP Webサービスのパッケージを使用している場合_ "net/http/pprof"
、その情報を直接デバッグ/ pprof /パスブラウザpprof見ることができます。
サードパーティの他のフレーム行くウェブが使用される場合、それは手動で対応するルーティングパスを追加する必要があり、例えばジンは、添加してもよいhttps://github.com/gin-contrib/pprof/blob/master/pprof.go。
GOツールのpprofツールで結合されたpprofパッケージ、することで、簡単に、分析のために、性能情報番組のために行くことができます。
最初の引用符を開始?ネット/ WebプロジェクトのHTTP /防止用の袋は、プロジェクトがポート8080でリッスンしています。
でスタートした後、ちょっとアナログWebリクエスト。hey -z 2m -c 10 -q 2 -H 'token: xxxxxxxxxx' 'http://127.0.0.1:8080/xxx'
実行時にリクエストしてくださいgo tool pprof test http://127.0.0.1:8080/v1/debug/pprof/profile
(バイナリファイルにコンパイルするWebサービスをテストする)、あなたはこの期間中にパフォーマンス情報を取得することができます。次のように:
File: engine
Type: cpu
Time: Aug 3, 2019 at 3:23pm (CST)
Duration: 30s, Total samples = 580ms ( 1.93%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 460ms, 79.31% of 580ms total
Showing top 10 nodes out of 210
flat flat% sum% cum cum%
120ms 20.69% 20.69% 120ms 20.69% syscall.syscall
110ms 18.97% 39.66% 110ms 18.97% runtime.pthread_cond_wait
60ms 10.34% 50.00% 60ms 10.34% runtime.pthread_cond_signal
40ms 6.90% 56.90% 40ms 6.90% runtime.kevent
40ms 6.90% 63.79% 40ms 6.90% runtime.nanotime
20ms 3.45% 72.41% 20ms 3.45% encoding/json.checkValid
20ms 3.45% 75.86% 20ms 3.45% runtime.notetsleep
10ms 1.72% 77.59% 10ms 1.72% encoding/json.(*decodeState).literalStore
10ms 1.72% 79.31% 10ms 1.72% encoding/json.stateInString
(pprof)
期間特徴:30代性能情報収集時間は、デフォルトでは30代で、パラメータは、実行pprofを指定することができるgo tool pprof test http://127.0.0.1:8080/debug/pprof/profile?seconds=60
時間を調整します。
pprofインタラクティブコマンドラインで、入力関数はトップ時間がかかりを表示することができ、結果として、各列は情報の関数を表します。最初の2列は、時間とCPU上で実行されているのパーセンテージの関数を表し、また、機能および動作時間及び占有サブ機能の比(第4列および第5列表し、第3列は、CPUの全ての機能の現在の累積割合であります累積積算値をいう)、前者の値が2以上でなければならない;最後の列は、関数の名前です。アプリケーションのパフォーマンスの問題場合は、上記の情報は私たちにどの機能の実行に費やした時間を伝えることができるはずです。
ウェブのコマンド入力は、それが全体のアプリケーション、ブラウザを直接開くことができ、より大きなブロック図は機能を表すの関数呼び出しSVG形式の図を生成することができる、機能が実行長く示します。
その他の便利なコマンドがありますが、あなたが表示するヘルプを入力することができます。
プロファイル情報の例としては、ゴルーチン、などのヒープとして、取得するだけでなく、他のデバッグURLから、追加情報を入手されますgo tool pprof http://127.0.0.1:8080/debug/pprof/heap
。
対話型のコマンドラインに加えて、コマンドパラメータ上-http追加し、また、Webページを提供pprofツールを行くgo tool pprof -http=":8081" engine http://127.0.0.1:8080/debug/pprof/profile
、あなたはWebサービスを通じてアプリケーション全体のための情報とパフォーマンスチャートの様々なを表示することができ、指定したポートでWebサービスを開始することができます図のように分析炎。
参考: