Go のマルチスレッド アプローチと、pthread、boost::thread、Java スレッドなどの他のアプローチの違いは何ですか?
4 に答える
IMO、Goのマルチスレッドを魅力的なものにしているのは通信機能です。通信インフラストラクチャ(ミューテックス、キューなど)を構築する必要があるpthreadとは異なり、Goではデフォルトで便利な形式で利用できます。
要するに、優れた通信機能(私がそう言えばErlangに似ている)のために、スレッドを使用することには「低摩擦」があります。
リファレンス コンパイラ (5g/6g/8g) では、マスター スケジューラ ( src/pkg/runtime/proc.c ) が N 個の OS スレッドを作成します。N は runtime.GOMAXPROCS(n) (デフォルト 1) によって制御されます。各スケジューラ スレッドは、新しいゴルーチンをマスター リストから取り出して実行を開始します。ゴルーチンは、システムコール (printf など) が行われるか、チャネルで操作が行われるまで実行され続けます。その時点で、スケジューラーは次のゴルーチンを取得し、中断した時点から実行します (参照)。 gosched() はsrc/pkg/runtime/chan.cで呼び出します)。
スケジューリングは、すべての意図と目的のために、コルーチンで実装されます。setjmp() と longjmp() を使用して同じ機能をストレート C で記述できます。Go (および軽量/グリーン スレッドを実装する他の言語) は、プロセスを自動化するだけです。
軽量スレッドの利点は、それがすべてユーザー空間であるため、「スレッド」の作成が非常に安価であり (小さなデフォルト スタックを割り当てる)、スレッドが互いに対話する方法の固有の構造により非常に効率的である可能性があることです。欠点は、それらが真のスレッドではないことです。つまり、すべてのスレッドが同時に実行されているように見える場合でも、単一の軽量スレッドがプログラム全体をブロックする可能性があります。
以前の回答で述べたように、go ルーチンは必ずしもシステム スレッドに対応しているわけではありませんが、今すぐマルチスレッドのパフォーマンスを向上させる必要がある場合は、次のものが役立つことがわかりました。
Go ランタイムの現在の実装は、デフォルトではこのコードを並列化しません。ユーザーレベルの処理専用のコアは 1 つだけです。システムコールで任意の数のゴルーチンをブロックできますが、デフォルトでは、いつでもユーザーレベルのコードを実行できるのは 1 つだけです。それはよりスマートになるはずであり、いつかはよりスマートになるでしょうが、それまではCPUの並列処理が必要な場合は、コードを同時に実行するゴルーチンの数をランタイムに伝える必要があります. これを行うには、関連する 2 つの方法があります。環境変数 GOMAXPROCS を使用するコア数に設定してジョブを実行するか、ランタイム パッケージをインポートして runtime.GOMAXPROCS(NCPU) を呼び出します。役立つ値は、ローカル マシン上の論理 CPU の数を報告する runtime.NumCPU() です。繰り返しになりますが、この要件は、スケジューリングと実行時間が改善されるにつれて廃止される予定です。
私の i5 プロセッサを最大限に活用するサンプル プログラムは次のとおりです (htop で 4 つのコアすべてを 100% で使用):
package main
import (
"fmt"
"time"
"runtime"
)
func main() {
runtime.GOMAXPROCS(4) // Set the maximum number of threads/processes
d := make(chan string)
go boring("boring!", d, 1)
go boring("boring!", d, 2)
go boring("boring!", d, 3)
go boring("boring!", d, 4)
for i := 0; i < 10; i++ {
time.Sleep(time.Second);
}
fmt.Println("You're boring; I'm leaving.")
}
func boring(msg string, c chan string, id int) {
for i := 0; ; i++ {
}
}
これは実際には何も「実行」しませんが、Java などの他の言語でマルチスレッド アプリケーションを作成する場合と比較して、いかに短く/簡単/単純であるかを確認してください。