をあなたが最初@Not_a_Golferの答えを読んで、彼はゴルーチンが予定されているかを理解するために提供されたリンクすべきです。私の答えは、具体的にネットワークI/Oに浸るようなものです。私はGoがどのように協力マルチタスクを達成するのかを理解していると思います。
すべてはゴルーチンで実行され、実際のOSスレッドではないので、コールはブロッキングコールのみを使用し、使用します。彼らは緑色の糸です。だから、IOコールの多くをブロックして、OSスレッドのようなあなたのメモリとCPUをすべて食べることはありません。
ファイルIOは単にsyscallsです。 Not_a_Golferはすでにそれをカバーしていました。 Goは実際のOSスレッドを使用してシステムコールを待機し、ゴルーチンが復帰するとブロックを解除します。 Here UNIX用のファイルread
を見ることができます。
ネットワークIOが異なります。ランタイムは、 "ネットワークポーラー"を使用して、どのゴルーチンがIO呼び出しからブロック解除するべきかを決定します。ターゲットOSによっては、使用可能な非同期APIを使用してネットワークIOイベントを待機します。コールはブロッキングのように見えますが、内部はすべて非同期で処理されます。
たとえば、read
をTCPソケットgoroutineで呼び出すと、最初にsyscallを使用して読み取ろうとします。まだ到着していなければ、それはブロックされ、それが再開されるのを待つ。ここでブロックすることは、ゴルーチンを待ち行列に入れて再開するのを待つパーキングを意味します。これは、ネットワークIOを使用すると、ブロックされたgoroutineが他のgoroutinesに実行をもたらす方法です。
func (fd *netFD) Read(p []byte) (n int, err error) {
if err := fd.readLock(); err != nil {
return 0, err
}
defer fd.readUnlock()
if err := fd.pd.PrepareRead(); err != nil {
return 0, err
}
for {
n, err = syscall.Read(fd.sysfd, p)
if err != nil {
n = 0
if err == syscall.EAGAIN {
if err = fd.pd.WaitRead(); err == nil {
continue
}
}
}
err = fd.eofError(n, err)
break
}
if _, ok := err.(syscall.Errno); ok {
err = os.NewSyscallError("read", err)
}
return
}
https://golang.org/src/net/fd_unix.go?s=#L237
データが到着するとネットワークポーラーを再開する必要があるゴルーチンを返します。あなたは実行できるゴルーチンを検索するherefindrunnable
関数を見ることができます。それはnetpoll
関数を呼び出し、これは再開可能なゴルーチンを返します。 kqueue
の実装はnetpoll
hereです。
C#でのasync/waitについては、非同期ネットワークIOは非同期API(WindowsのIO完了ポート)も使用します。何かが到着すると、OSは現在のSynchronizationContext
に継続を入れるスレッドプールの完了ポートスレッドの1つでコールバックを実行します。ある意味では、いくつかの類似点があります(パーキング/パーク解除は、継続を呼び出すようなものですが、はるかに低いレベルです)。しかし、これらのモデルは、実装はもちろんのこと、非常に異なります。デフォルトでは、Goroutinesは特定のOSスレッドにバインドされておらず、いずれかのスレッドで再開できますが、問題はありません。対処するUIスレッドはありません。非同期/待機は、具体的には、同じOSスレッドで作業を再開する目的で、SynchronizationContext
を使用して行われます。また、緑のスレッドや別のスケジューラがないので、async/awaitは、あなたの関数を複数のコールバックに分割して実行する必要があります。SynchronizationContext
は実行されるべきコールバックのキューをチェックする無限ループです。あなたはそれを自分で実装することもできます。本当に簡単です。
Go実行時スケジューラ(ネットワーク上のepoll、Windows上のIOCPなど)はネットワークI/Oのシステムコールのみを多重化しています(Go 1.6以下)。ディスク、シリアルなどに衝突するすべてのI/Oシステムコールは、それぞれ単一のOSスレッドを占有します。これが良いか悪いかはGoの開発者のコミュニティで議論の余地があります。現在のコンセンサスは、一般的な非同期I/Oをユーザーに提供するのがうまくいくと思われますが、実用的な立場からは実際には役に立たないと思われます。 – kostix
... - 1000個のゴルーチンがある場合async I/Oと同時に同じディスクドライブに書き込むことは本当に役に立たないでしょう。専用のライターとバッファードチャネルを使用してください。サイドノート:基盤OSの非同期/ポーラーインタフェースを公開するサードパーティ製のパッケージが存在します。 – kostix