コンピュータ サイエンスにおいて、イベント ループ(メッセージ ディスパッチャ、メッセージ ループ、メッセージ ポンプ、または実行ループとも呼ばれる) は、プログラム内でイベントまたはメッセージを待機してディスパッチするプログラミング構造または設計パターンです。イベント ループは、内部または外部の「イベント プロバイダ」(通常はイベントが到着するまで要求をブロックします) に要求を送信し、関連するイベント ハンドラ(「イベントをディスパッチ」) を呼び出すことによって機能します。
また、 Web サーバーなどのサーバーにもよく実装されています。
イベント ループは、イベント プロバイダーがファイル インターフェイスに従う場合、リアクターと組み合わせて使用できます。ファイル インターフェイスは選択または「ポーリング」(実際のポーリングではなく、Unix システム コール) できます。イベント ループは、ほとんどの場合、メッセージ発信元と非同期で動作します。
よくあることですが、イベント ループがプログラムの中心的な制御フロー構造を形成する場合、メイン ループまたはメイン イベント ループと呼ばれることがあります。このようなイベント ループはプログラム内で制御の最高レベルにあるため、このタイトルは適切です。
メッセージパッシング
メッセージ ポンプは、プログラムのメッセージ キュー(通常は基盤となるオペレーティング システムによって割り当てられ、所有される) からプログラムにメッセージを「送り込む」と言われています。厳密な意味では、イベント ループはプロセス間通信を実装する方法の 1 つです。実際、メッセージ処理は、Mach オペレーティング システムのカーネル レベルのコンポーネントを含む多くのシステムに存在します。イベント ループは、メッセージ パッシングを使用するシステムの特定の実装手法です。
代替デザイン
このアプローチは、他のいくつかの代替案とは対照的です。
- 従来、プログラムは単に 1 回実行され、その後終了していました。このタイプのプログラムは、コンピューティングの初期の頃には非常に一般的でしたが、ユーザーとの対話性はまったくありませんでした。これは、特にコマンドライン駆動型プログラムの形で、今でも頻繁に使用されています。すべてのパラメータは事前に設定され、プログラムの起動時に 1 回で渡されます。
- メニュー駆動型設計。メイン ループがまだある場合もありますが、通常の意味でのイベント駆動型とは考えられていません。 [要出典]代わりに、実行したいタスクが唯一の選択肢になるまで、ユーザーには選択肢がどんどん狭まって表示されます。メニューを介した対話機能は限定されています。
使用法
グラフィカル ユーザー インターフェイスが主流であるため、最近のアプリケーションのほとんどにはメイン ループが備わっています。ルーチンはget_next_message()通常、オペレーティング システムによって提供され、メッセージが利用可能になるまでブロックされます。したがって、ループに入るのは、処理するものがあるときだけです。
関数メイン
初期化()
メッセージ!=終了中
メッセージ:= get_next_message()
process_message(メッセージ)
終了関数を終了しながら
終了
ファイルインターフェース
Unixでは、「すべてがファイル」というパラダイムにより、ファイルベースのイベント ループが自然に発生します。ファイルの読み取りと書き込み、プロセス間通信、ネットワーク通信、デバイス制御はすべて、ファイル記述子によって識別されるターゲットを使用して、ファイル I/O を使用して実行されます。selectおよびpollシステム コールを使用すると、一連のファイル記述子の状態の変化 (たとえば、データが読み取り可能になったときなど) を監視できます。
たとえば、継続的に更新されるファイルから読み取り、その内容をX Window Systemに表示し、ソケット ( Unix ドメインまたはBerkeley ) を介してクライアントと通信するプログラムを考えます。
def main ():
file_fd = open ( "logfile.log" )
x_fd = open_display ( )
construct_interface ()
while True :
rlist , _ , _ = select.select ([ file_fd , x_fd ], [], []): if file_fd in rlist : data = file_fd.read ( ) append_to_display ( data ) send_repaint_message ( ) if x_fd in rlist : process_x_messages ( )
シグナルの取り扱い
Unix でファイル インターフェイスに準拠していない数少ないものの 1 つに、非同期イベント (シグナル) があります。シグナルはシグナル ハンドラーで受信されます。シグナル ハンドラーは、タスクの残りの部分が中断されている間に実行される、小さくて制限されたコードです。タスクが でブロックされている間にシグナルが受信され処理されると、select はEINTRselect()で早期に戻ります。タスクがCPU バウンドである間にシグナルが受信されると、シグナル ハンドラーが戻るまでタスクは命令間で中断されます。
したがって、シグナルを処理する明白な方法は、シグナル ハンドラがグローバル フラグを設定し、select()呼び出しの直前と直後にイベント ループでフラグをチェックすることです。フラグが設定されている場合は、ファイル記述子のイベントと同じ方法でシグナルを処理します。残念ながら、これにより競合状態が発生します。フラグのチェックと の呼び出しの間にシグナルがすぐに到着した場合、他の何らかの理由 (たとえば、イライラしたユーザーによって中断されるなど) で が戻る
select()までそのシグナルは処理されません。select()
POSIXが到達した解決策は呼び出しです。pselect()これは に似ていますが、シグナル マスクを記述するselect()追加のパラメータを取ります。これにより、アプリケーションはメイン タスクでシグナルをマスクし、呼び出しの期間中はマスクを削除して、アプリケーションがI/O バウンドの場合にのみシグナル ハンドラが呼び出されるようにすることができます。ただし、 の実装は常に信頼できるわけではありません。2.6.16 より前のバージョンの Linux にはシステム コールがありません。[1] glibc に、まったく同じ競合状態になりやすい方法でそれをエミュレートさせることは、これを回避することを目的としています。
sigmaskselect()pselect()pselect()pselect()
より移植性の高い代替ソリューションは、セルフパイプトリック[2]を使用して非同期イベントをファイルベースのイベントに変換することです。セルフパイプトリックでは、 「シグナルハンドラは、もう一方の端がselect()メインプログラムによって監視されているパイプにバイトを書き込みます。」 [3] Linuxカーネルバージョン2.6.22では、特別なファイル記述子を介してシグナルを受信できる新しいシステムコールsignalfd()が追加されました。
実装
HTML/JavaScript
Web ページとその JavaScript は通常、シングルスレッドの Web ブラウザープロセスで実行されます。ブラウザー プロセスは、キューからのメッセージを1 つずつ処理します。特定のメッセージには、JavaScript関数または別のブラウザー イベントが関連付けられている場合があります。ブラウザー プロセスがメッセージの処理を終了すると、キュー内の次のメッセージに進みます。
Windows アプリケーション
Microsoft Windowsオペレーティング システムでは、ユーザーと対話するプロセスは、着信メッセージを受け入れてそれに応答する必要があります。これは、そのプロセス内のメッセージ ループによってほぼ必然的に実行されます。Windows では、メッセージは、オペレーティング システムで作成され、課されるイベントと同等です。イベントには、ユーザー操作、ネットワーク トラフィック、システム処理、タイマー アクティビティ、プロセス間通信などがあります。非対話型の I/O のみのイベントの場合、Windows にはI/O 完了ポートがあります。I/O 完了ポート ループはメッセージ ループとは別に実行され、そのままではメッセージ ループと対話しません。
ほとんどのWin32 アプリケーションの「心臓部」はWinMain() 関数で、ループで GetMessage() を呼び出します。GetMessage() は、メッセージまたは「イベント」が受信されるまでブロックします (非ブロッキングの代替手段として関数 PeekMessage() を使用)。いくつかのオプション処理の後、DispatchMessage() を呼び出します。DispatchMessage() は、メッセージを関連するハンドラー ( WindowProcとも呼ばれます) にディスパッチします。通常、特別な WindowProc() を持たないメッセージは、既定のDefWindowProcにディスパッチされます。DispatchMessage() は、メッセージのHWND ハンドルの WindowProc を呼び出します(RegisterClass() 関数で登録されます)。
メッセージの順序
最近のバージョンの Microsoft Windows では、メッセージがシステムとその周辺機器によって認識された順序でアプリケーションのメッセージ ループに配信されることがプログラマーに保証されています。この保証は、マルチスレッドアプリケーションの設計上の結果を考慮する場合に不可欠です。
ただし、常に最後に受信されるメッセージや、文書化された優先度が異なるメッセージなど、一部のメッセージには異なるルールがあります。[4]
X ウィンドウ システム
Xlib イベント ループ
Xlib を直接使用するXアプリケーションはXNextEvent、関数ファミリーを中心に構築されます。XNextEventイベントがイベント キューに表示されるまでブロックし、イベントが表示されるとアプリケーションがそれを適切に処理します。Xlib イベント ループはウィンドウ システム イベントのみを処理します。他のファイルやデバイスを待機する必要があるアプリケーションは、などのプリミティブから独自のイベント ループを構築できますConnectionNumberが、実際にはマルチスレッドを使用する傾向があります。
Xlib を直接使用するプログラムはほとんどありません。より一般的なケースでは、Xlib に基づく GUI ツールキットが通常、イベントの追加をサポートします。たとえば、Xt IntrinsicsXtAppAddInput()に基づくツールキットには、およびがありますXtAppAddTimeout()。
Xアプリケーションが任意の状態( 内など)で中断される可能性があるため、シグナルハンドラからXlib関数を呼び出すのは安全ではないことに注意してください。X11R5 XNextEvent、X11R6、およびXtの解決策については[1]を参照してください。
GLib イベント ループ
GLibイベント ループは元々 GTKで使用するために作成されましたが、現在ではD-Busなどの非 GUI アプリケーションでも使用されています。ポーリングされるリソースは、アプリケーションが関心を持つファイル記述子のコレクションです。シグナルが到着するか、タイムアウトが経過すると (アプリケーションがタイムアウトまたはアイドル タスクを指定した場合など) 、ポーリング ブロックは中断されます。GLibにはファイル記述子と子終了イベントのサポートが組み込まれていますが、準備、チェック、ディスパッチ モデルで処理できる任意のイベントのイベント ソースを追加できます。[2]
GLib イベント ループ上に構築されるアプリケーション ライブラリには、GStreamerやGnomeVFSの非同期 I/Oメソッドが含まれますが、GTK は依然として最もよく知られているクライアント ライブラリです。ウィンドウ システムからのイベント( Xでは、Xソケットから読み取られます) は、GDKによってGTK イベントに変換され、アプリケーションのウィジェット オブジェクトで GLib シグナルとして発行されます。
macOS Core Foundation 実行ループ
スレッドごとに 1 つの CFRunLoop が許可され、任意の数のソースとオブザーバーを接続できます。ソースは実行ループを介してオブザーバーと通信し、キューイングとメッセージのディスパッチを整理します。
CFRunLoop はCocoaでは NSRunLoop として抽象化されており、これにより任意のメッセージ (非反射ランタイムでの関数呼び出しに相当) を任意のオブジェクトにディスパッチするためにキューに入れることができます。
参照
- 非同期I/O
- イベント駆動型プログラミング
- プロセス間通信
- メッセージパッシング
- ゲームプログラミングにおけるゲームループ
参考文献
外部リンク
- MFC メッセージとコマンド ルーティングの迷路をさまよう
- メッセージとメッセージ キューの使用 (MSDN)
- ウィンドウ プロシージャの使用 (MSDN)
- ウィンドウプロシージャ (MSDN)
