web-dev-qa-db-ja.com

ゴルーチンはどのように機能しますか? (または:ゴルーチンとOSスレッドの関係)

Syscallを呼び出している間、他のゴルーチンはどのように実行し続けることができますか? (GOMAXPROCS = 1を使用する場合)
私が知る限り、syscallを呼び出すと、スレッドはsyscallが戻るまで制御を放棄します。 Goは、syscallのブロッキングゴルーチンごとにシステムスレッドを作成せずに、この並行性をどのように実現できますか?

ドキュメント から:

ゴルーチン

既存の用語(スレッド、コルーチン、プロセスなど)が不正確な意味合いを伝えるため、これらはゴルーチンと呼ばれます。ゴルーチンには単純なモデルがあります。これは、同じアドレス空間で他のゴルーチンと同時に実行される関数です。軽量であり、スタックスペースの割り当てとほとんど同じです。また、スタックは小さく起動するため、安価であり、必要に応じてヒープストレージを割り当てて(そして解放して)成長します。

ゴルーチンは複数のOSスレッドに多重化されるため、I/Oを待機している間など、ブロックする必要がある場合、他のスレッドは引き続き実行されます。それらの設計は、スレッドの作成と管理の複雑さの多くを隠します。

54
omribahumi

ゴルーチンがブロックしている場合、ランタイムは新しいOSスレッドを起動して、ブロックしているゴルーチンがブロックを停止するまで、他のゴルーチンを処理します。

参照: https://groups.google.com/forum/#!topic/golang-nuts/2IdA34yR8gQ

35
OneOfOne

わかりました、それで私が学んだことはここにあります:あなたが生のシステムコールをしているとき、Goは実際にブロッキングゴルーチンごとにスレッドを作成します。たとえば、次のコードを検討してください。

package main

import (
        "fmt"
        "syscall"
)

func block(c chan bool) {
        fmt.Println("block() enter")
        buf := make([]byte, 1024)
        _, _ = syscall.Read(0, buf) // block on doing an unbuffered read on STDIN
        fmt.Println("block() exit")
        c <- true // main() we're done
}

func main() {
        c := make(chan bool)
        for i := 0; i < 1000; i++ {
                go block(c)
        }
        for i := 0; i < 1000; i++ {
                _ = <-c
        }
}

Ubuntu 12.04を実行すると、そのプロセスに対して1004スレッドが報告されました。

一方、GoのHTTPサーバーを利用して1000個のソケットを開くと、4つのオペレーティングシステムスレッドのみが作成されました。

package main

import (
        "fmt"
        "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}

func main() {
        http.HandleFunc("/", handler)
        http.ListenAndServe(":8080", nil)
}

そのため、ブロッキングシステムコールごとにIOLoopとスレッドが混在しています。

26
omribahumi

できません。 GOMAXPROCS = 1のときに一度に実行できるゴルーチンは、そのゴルーチンがシステムコールを実行しているかどうかに関係なく、1つだけです。

ただし、ソケットI/Oなど、タイマーを待っているほとんどのブロックシステムコールは、Goから実行された場合、システムコールではブロックされません。これらは、Goランタイムによってepoll、kqueue、またはOSがI/Oの多重化に提供する同様の機能に多重化されます。

Epollのようなものと多重化できない他の種類のブロッキングシステムコールについては、GOMAXPROCSの設定に関係なく、Goは新しいOSスレッドを生成します(Go 1.1の状態でしたが、状況が変わるかどうかはわかりません)

13
nos

プロセッサ番号とスレッド番号を区別する必要があります。物理プロセッサよりも多くのスレッドを持つことができるため、マルチスレッドプロセスは引き続き単一のコアプロセッサで実行できます。

引用したドキュメントが説明しているように、ゴルーチンはスレッドではありません。それは単に、スタック領域のチャンク専用のスレッドで実行される関数です。プロセスに複数のスレッドがある場合、この関数はどちらのスレッドでも実行できます。そのため、他の理由(syscall、I/O、同期)でブロックしているゴルーチンをそのスレッドに入れ、他のルーチンを別のルーチンで実行できます。

5
Elwinar