従来のプログラミング モデルでは、I/O 操作は通常のローカル関数呼び出しに似ています。プログラムは関数が実行される前にブロックされ、実行を続行できません。 I/O のブロッキングは、以前のタイム スライス モデルから始まりました。このモデルでは、各プロセスは独立した人間のようなものであり、各人間は通常、同時に 1 つのことしか実行できず、待機する必要があります。前のことを終えたら、次に何をするかを決めることができますか。しかし、コンピュータ ネットワークやインターネットで広く使用されているこの「1 ユーザー、1 プロセス」モデルには拡張性がありません。複数のプロセスを管理すると、大量のメモリが消費され、コンテキストの切り替えにも多くのリソースが占有されます。これらはオペレーティング システムにとって大きな負担となり、プロセスの数が増えるとシステムのパフォーマンスが急激に低下します。
マルチスレッドは、同じプロセス内の他のスレッドとメモリを共有する軽量プロセスであり、スレッド While のときに複数のスレッドを同時に実行するために使用されます。 I/O 操作を待機している間、他のスレッドが CPU を引き継ぐことができます。I/O 操作が完了すると、前に待機していたスレッドが起動されます。つまり、実行中のスレッドを中断して、後で再開することができます。さらに、一部のシステムでは、マルチコア CPU の異なるコア上でスレッドを並行して実行できます。
プログラマは、スレッドが特定の時間に実行されるかどうかを知ることができないため、共有メモリへの同時アクセスを処理する際には、次のような同期プリミティブを使用する必要があります。ロックまたはセマフォを使用して、スレッドを特定の動作および計画で強制的に実行します。スレッド間の共有状態に大きく依存するアプリケーションは、非常にランダムで発見が難しい奇妙な問題を引き起こす傾向があります。
もう 1 つの方法は、マルチスレッド コラボレーションを使用することです。スレッドの実行計画を個人的に制御するため、CPU を明示的に解放し、その時間を他のスレッドに与える必要があります。しかし、プログラムが複雑になり、エラーが発生する可能性も高まり、マルチスレッドの問題は避けられません。
イベント駆動型プログラミングとは
イベント駆動型プログラミング (Evnet 駆動型プログラミング) は、イベントがプログラムの実行フローを決定するプログラミング スタイルであり、イベント コールバックはイベント コールバックによって呼び出されます。データベースがクエリ結果を返したり、ユーザーがボタンをクリックしたりするなど、特定のイベントが発生します。
従来のブロッキング I/O プログラミング モデルでは、データベース クエリは次のようになることを思い出してください:
do_something_with(結果);
イベント駆動型モデルでは、このクエリは次のようになります:
do_something_with(結果);
}
query('SELECT * FROM 投稿 WHERE id = 1', query_finished);
まず、query_finished という関数を定義します。この関数には、クエリが完了した後に何をするかが含まれています。次に、この関数をパラメーターとしてクエリ関数に渡します。クエリが実行されると、クエリ結果を返すだけでなく、query_finished が呼び出されます。
関心のあるイベントが発生すると、単に結果値を返すのではなく、定義した関数が呼び出されます。このプログラミング モデルは、イベント駆動型プログラミングまたは非同期プログラミングと呼ばれます。これは、Node の最も明白な機能の 1 つです。このプログラミング モデルは、I/O 操作の実行時に現在のプロセスがブロックされないことを意味します。したがって、操作が完了すると、複数の I/O 操作が並行して実行されます。対応するコールバック関数が呼び出されます。
イベント駆動型プログラミングの最下層はイベント ループに依存します。イベント ループは基本的に、イベント検出とイベント プロセッサのトリガーという 2 つの関数を継続的に呼び出す構造です。各ループでは、イベント ループ メカニズムがどのイベントが発生したかを検出する必要があり、イベントが発生すると、対応するコールバック関数を見つけて呼び出します。
イベント ループは、プロセス内で実行される単なるスレッドです。イベントが発生すると、イベント ハンドラーは単独で実行でき、中断されません。つまり、
1. 特定の瞬間に実行できるイベント コールバック関数は 1 つまでです
2.
これにより、開発者はスレッドの同期や共有メモリの同時変更について心配する必要がなくなります。
よく知られた秘密:
システム プログラミング コミュニティの人々は、イベント駆動型プログラミングが、大量のコンテキストを保存する必要がないため、大量のメモリを節約できるため、同時実行性の高いサービスを作成する最良の方法であることを長い間知っていました。コンテキストの切り替えがそれほど多くないため、実行時間が大幅に節約されます。
この概念は徐々に他のプラットフォームやコミュニティに浸透し、Ruby の Event マシン、Perl の AnyEvnet、Python の Twisted などのいくつかの有名なイベント ループ実装が登場しました。これらに加えて、他にも多くの実装と言語があります。
これらのフレームワークを使用して開発するには、フレームワークおよびフレームワーク固有のクラス ライブラリに関連する特定の知識を学ぶ必要があります。たとえば、イベント マシンを使用する場合、ノンブロッキングの利点を享受するには、使用を避ける必要があります。同期クラス ライブラリであり、イベント マシンには非同期クラス ライブラリのみを使用できます。ブロッキング ライブラリ (Ruby の標準ライブラリのほとんどなど) を使用すると、イベント ループが常にブロックされ、場合によっては I/O イベントの処理が妨げられるため、サーバーは最適なスケーラビリティを失います。
ノードはもともとノンブロッキング I/O サーバー プラットフォームとして設計されているため、一般に、ノード上で実行されるすべてのコードはノンブロッキングであると想定する必要があります。 JavaScript は非常に小さく、I/O モデルを強制しないため (標準 I/O ライブラリがないため)、Node はレガシーな問題のない非常に純粋な環境で構築されます。
Node と JavaScript が非同期アプリケーションを簡素化する方法
Node の作者である Ryan Dahl は、最初は C を使用してこのプロジェクトを開発しましたが、関数呼び出しのコンテキストの維持が複雑すぎるため、コードが非常に複雑になることがわかりました。その後、彼は Lua に切り替えましたが、Lua にはすでにいくつかのブロッキング I/O ライブラリがあり、ブロッキングと非ブロッキングを混在させると開発者が混乱し、多くの人がスケーラブルなアプリケーションを構築できなくなる可能性があるため、Lua も放棄されました。最後に、彼は JavaScript に目を向けました。JavaScript のクロージャと第 1 レベルのオブジェクト関数により、JavaScript はイベント駆動型プログラミングに非常に適したものになりました。 JavaScript の魔法は、Node が非常に人気がある主な理由の 1 つです。
クロージャとは
クロージャーは特別な関数として理解できますが、それが定義されているスコープ内の変数を継承し、アクセスすることができます。コールバック関数をパラメータとして別の関数に渡すと、そのコールバック関数が後で呼び出されるときに、コールバック関数が定義されているコンテキストとその親コンテキストを実際に記憶しているのです。正常にアクセスできます。この強力な機能が Node の成功の核心です。
次の例は、JavaScript クロージャーが Web ブラウザーでどのように機能するかを示しています。ボタンのスタンドアロン イベントをリッスンしたい場合は、次のようにすることができます:
document.getElementById('myButton').onclick = function() {
クリック数 = 1;
alert("clickCount" 回クリックされました。");
};
jQuery を使用する場合の動作は次のとおりです:
$('button#mybutton').click(function() {
クリック数 ;
alert('clickCount' 回クリックされました。');
});
JavaScript では、関数はファーストクラスのオブジェクトです。つまり、関数をパラメータとして他の関数に渡すことができます。上記の 2 つの例では、前者は関数を別の関数に割り当て、後者はその関数をパラメーターとして別の関数に渡します。クリック イベントのハンドラー関数 (コールバック関数) は、関数が存在するコード ブロック内のすべての変数にアクセスできます。が定義されており、この場合、親クロージャー内で定義された clickCount 変数にアクセスできます。
clickCount 変数はグローバル スコープ (JavaScript の最も外側のスコープ) にあり、ユーザーがボタンをクリックした回数を保存します。変数をグローバル スコープに格納するのは、通常、グローバル スコープと競合しやすいため、悪い習慣です。他のコードでは、変数が使用されるローカル スコープに変数を配置する必要があります。ほとんどの場合、コードを関数でラップするだけで、追加のクロージャを作成するのと同じことになるため、次のようにグローバル環境の汚染を簡単に回避できます。
$('button#mybutton').click(function() {
クリック数 ;
alert('clickCount' 回クリックされました。');
}());
イベント駆動型プログラミング モデルでは、最初にイベントの発生後に実行されるコードを記述し、次にそのコードを関数に組み込み、最後にその関数をパラメーターとして呼び出し元に渡します。この関数は後で呼び出されます。呼び出し側関数。
JavaScript では、関数は独立した定義ではなく、関数が宣言されているスコープのコンテキストも記憶します。このメカニズムにより、JavaScript 関数は関数が定義されているコンテキストとすべての親コンテキストにアクセスできます。 。
コールバック関数をパラメーターとして呼び出し元に渡すと、この関数は後で呼び出されます。コールバック関数が定義されているスコープが終了した場合でも、コールバック関数が呼び出されるときは、終了したスコープとその親スコープ内のすべての変数に引き続きアクセスできます。最後の例と同様に、コールバック関数は jQuery の click() 内で呼び出されますが、依然として clickCount 変数にアクセスできます。
クロージャの魔法は前に示しました。状態変数を関数に渡すと、JavaScript のクロージャ メカニズムによって状態を維持することができます。
概要イベント駆動型プログラミングは、イベント トリガーを通じてプログラムの実行フローを決定するプログラミング モデルです。プログラマは、関心のあるイベントのコールバック関数 (イベント ハンドラーと呼ばれることが多い) を登録し、イベントが発生するとシステムが登録されたイベント ハンドラーを呼び出します。このプログラミング モデルには、従来のブロッキング プログラミング モデルにはない多くの利点があります。これまで、同様の機能を実現するには、マルチプロセス/マルチスレッドを使用する必要がありました。
JavaScript は、その関数と最初のタイプのオブジェクトのクロージャ プロパティによりイベント駆動型プログラミングに適しているため、強力な言語です。