すべてのコードが非ブロッキングであるnode.jsの観点から来ています。
Goでは、チャネルを使用してノンブロッキングを簡単に実現できます。
goでnode.jsタイプのサーバーを作成している場合、それを非ブロッキングにすることは理にかなっていますか?たとえば、データベースのconnect()関数がチャネルを返すのは、接続が発生するのを待っている間のブロッキングとは異なります。
私には、これは正しいアプローチのようです
だが ...
ブロッキングとノンブロッキングは、実際にはパフォーマンスに関するものではなく、インターフェースに関するものです。実行のシングルスレッドがある場合、ブロッキング呼び出しにより、プログラムが待機している間、プログラムが有用な作業を行えなくなります。ただし、複数の実行スレッドがある場合、そのスレッドをブロックしたままにして別のスレッドで有用な作業を行うことができるため、ブロッキング呼び出しは実際には問題になりません。
Goでは、I/Oでブロックされると、ゴルーチンが別のゴルーチンに交換されます。 Goランタイムは非ブロッキングI/Oシスコールを使用して、オペレーティングシステムがスレッドをブロックするのを回避し、最初のスレッドがI/Oを待機している間に別のgoroutineを実行できるようにします。
Goroutineは本当に安いので、ノンブロッキングスタイルのコードを書く必要はありません。
ブロッキング関数を記述します。この言語を使用すると、同期呼び出しを非同期呼び出しに簡単に変換できます。
関数を非同期的に呼び出す場合は、goステートメントを使用します。このようなもの:
c := make(chan bool)
go func() {
blockingFunction()
c <- true
}()
// do some other stuff here while the blocking function runs
// wait for the blocking function to finish if it hasn't already
<-c
Goでは、システムコールは、OSがサポートする最も効率的なメカニズム(epoll
など)を使用して、非ブロッキング方法で実装されます。呼び出しの結果を待機している間に実行する他のコードがない場合は、スレッドをブロックします(実行する適切な処理がないため)が、アクティブな代替ゴルーチンがある場合は、代わりに実行されます。
(jsでの使用に慣れている)コールバックでは、基本的に同じ基本的なメカニズムを使用できますが、プログラマーには間違いなくより多くの精神体操が必要です。
Goでは、関数呼び出しの後に実行するコードは、コールバックとして定義されているのではなく、関数呼び出しの直後に指定されています。実行パスと並行して実行するコードは、チャネルを介して通信するゴルーチンにラップする必要があります。
一般的なWebサーバータイプのアプリケーションの場合、すべてを非同期にすることではないことをお勧めします。いくつかの理由があります。
非同期コードよりもシリアルブロッキングコードの方が推論しやすい(バグが見やすい)
golangのエラー処理は、defer()、panic()、recover()に基づいているため、おそらく100%非同期コードでは必要な結果が得られません。
注意しないと、ゴルーチンがリークする可能性があります [1つのディスカッション] 。非同期動作が多いほど、これらのタイプの問題を追跡することが難しくなり、発生する可能性が高くなります。
1つの戦略は、非同期性を高いレベルに集中させ、他のすべてをブロックすることです。したがって、「リクエストハンドラ」ブロブとは論理的に異なる「データベースハンドラ」ブロブがある可能性があります。どちらも別々のゴルーチンで実行され、チャネルを使用して通信します。しかし、「データベースハンドラ」内では、データベース接続を確立して各クエリを実行する呼び出しがブロックされています。
100%非同期または0%非同期を選択する必要はありません。
ブロッキングインターフェイスは、常にノンブロッキングインターフェイスよりもシンプルで優れています。 Goの優れた点は、並行(および並列)コードをシンプルで、簡単に推論できるブロッキングスタイルで記述できることです。
非ブロッキングプログラミングの方法はすべて、人々が使用している言語(特にJavaScript)の不備によるものであり、非ブロッキングプログラミングの方が本質的に優れているからではありません。