イベントループイベントループ (event loop)、メッセージディスパッチャ (message dispatcher)、メッセージループ (message loop)、メッセージポンプ (message pump)、ランループ (run loop) とは、プログラム内でイベントやメッセージを待ち受け、それらをディスパッチ(配送)する構成要素である。内部または外部の「イベントプロバイダー」(通常、イベントが到着するまで要求をブロックする)に要求することで動作し、次いで適当なイベントハンドラー (event handler) を呼び出す(イベントのディスパッチ)。イベントプロバイダーが後述のファイルインタフェースに従う場合、イベントループは reactor と連携する形で使われることがあり、 イベントループはプログラムの中心的制御構造となっていることが多い。そのためそれをメインループ (main loop) またはメインイベントループ (main event loop) とも呼ぶ。そのようなプログラムではイベントループが最上位の制御構造となっており、そのため「メイン」と名づけられている。 メッセージパッシングメッセージポンプという呼称は、プログラムのメッセージキュー(通常OSが割り当て、OSが所有する)からメッセージを汲み上げ、そのプログラム内で処理することに由来する。厳密には、イベントループはプロセス間通信の実装法の1つである。実のところメッセージ処理は多くのシステムに存在し、例えばMachのカーネルレベルのコンポーネントにもある。イベントループはメッセージを使用するシステムの実装技法の1つである。 代替手法この手法は以下のような他の手法とは対照的である:
例GUIが主流となったため、多くのアプリケーションがメインループを持つようになった。 function main initialize() while message != quit message := get_next_message() process_message(message) end while end function ファイルインタフェースUNIXでは「あらゆるものはファイルである」というパラダイムにより、ファイルベースのイベントループが自然に生まれた。ファイルの読み書きだけでなく、プロセス間通信、ネットワーク通信、デバイス制御が全てファイルI/Oで行われ、対象はファイル記述子で指定される。selectおよびpollシステムコールを使えば、複数のファイル記述子の状態変化を同時に監視でき、読み込むべきデータが到着したことを検知できる。 例として、継続的に更新されるファイルから読み込んでその内容を X Window System に表示するPythonプログラムを示す。クライアントとはソケットを通じて通信する。 main():
file_fd = open ("logfile")
x_fd = open_display ()
construct_interface ()
while changed_fds = select ({file_fd, x_fd}):
if file_fd in changed_fds:
data = read_from (file_fd)
append_to_display (data)
send_repaint_message ()
if x_fd in changed_fds:
process_x_messages ()
シグナル処理UNIXでファイルインタフェースに従わない数少ない例として、非同期イベント(シグナル)がある。シグナルはシグナルハンドラで受信する。シグナルハンドラは小さな制限されたコードであり、それが動作中はプログラム本体の処理はサスペンドされる。 したがってシグナルを考慮するには、シグナルハンドラで大域変数のフラグをセットし、イベントループの この問題を解決するため、POSIXではpselectシステムコールを提供している。これはselectに似ているが より汎用的な代替技法として、非同期イベントをself-pipe trickと呼ばれる技法でファイルベースのイベントに変換してやる技法がある[1]。これはシグナルハンドラでパイプに1バイトを書き込み、そのパイプのもう一方の端を主プログラムがselectで監視するという技法である[2]。Linuxカーネル 2.6.22では、 実装例WindowsアプリケーションWindowsにてユーザーとやりとりするプロセスを動作させる場合、イベントに応答するためのメッセージループが必須である。Windowsではイベントとメッセージは同等視される[注釈 1]。イベントとしては、ユーザーとのやりとり、ネットワークのトラフィック、システム処理、タイマー、プロセス間通信などがある。対話型でないI/Oのみのイベントについては、I/O完了ポートがある。I/O完了ポートのループはメッセージループとは別に動作し、メッセージループと相互作用することがない。 大抵のWin32アプリケーションの「心臓部」は 以下はMicrosoft Docs (旧MSDNライブラリ) に記載されている、メッセージループの実装例のひとつである[5]: MSG msg;
BOOL bRet;
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
そのほか、ブロッキングせずにメッセージキューからメッセージを読み取る メッセージの順序性より最近[いつ?]のWindowsでは、システムや周辺機器が受信した順序でメッセージループにメッセージが到達することを保証している。これはマルチスレッドアプリケーションの設計で必須となる。 ただし、一部のメッセージには異なる規則が適用され、常に最後に受信されるメッセージや文書化された特別な優先順位で受信されるメッセージがある[7]。 フレームワークMFC、Windows Forms、WPFといったアプリケーションフレームワークでは、ライブラリ側で既定のメッセージループの実装を持っているため、通常はアプリケーションコードで明示的に記述する必要はない。アプリケーションで必要となるイベントハンドラーのみを記述していく「イベント駆動型プログラミング」のスタイルを用いて効率的に開発できる。ただし、必要に応じてメッセージループを詳細にカスタマイズしたり[8]、異なるフレームワークを相互運用したりするためのAPIも用意されている[9][10]。 XlibのイベントループXlibを直接使用するXアプリケーションは、 Xlibを直接使用するプログラムは少ない。より一般的にはXlib上に構築されたGUIツールキットを使用する。例えば、X Toolkit Intrinsics (Xt) 上のツールキットでは なお、シグナル受信時の状態が不定であるため、シグナルハンドラからXlib関数を呼び出すのは危険である[11]。 GLibのイベントループGLibのイベントループはGTK+(現・GTK)向けに作られたが、今ではD-BusなどのGUI以外のアプリケーションでも使われている。ファイル記述子群で監視したいリソースを指定する。シグナルを受信するかタイムアウトすると、ブロック状態から復帰する。GLibはファイル記述子と子プロセス終了のイベントを組み込みでサポートしているが、prepare-check-dispatch モデルの対象として任意のイベントを加えることが可能である[12]。 GLibのイベントループを使っているアプリケーションライブラリとしては、GStreamerやGnomeVFSの非同期IOメソッド群があるが、最大のクライアントライブラリはGTKである。ウィンドウシステムからのイベント(Xの場合、Xのソケットからの読み込み)はGTK+イベントに変換され、アプリケーションのウィジェットオブジェクト上のGLibシグナルとして発せられる。 macOSのループmacOSではスレッド毎に1つのCFRunLoopがあり、それに任意個のソース(イベント源)とオブザーバ(ハンドラ)を対応させることができる。そのループがメッセージのキューイングとディスパッチを行い、それを通してソースとオブザーバがやりとりする。 CocoaではCFRunLoopがNSRunLoopに抽象化されており、任意のメッセージをキューイングして任意のオブジェクトにディスパッチできる。 AndroidのメッセージループJava言語向けのAndroid SDKにおけるアプリケーションフレームワークには、メッセージを表現する Android NDKでは、POSIXパイプと 脚注注釈出典
関連項目外部リンク |
Portal di Ensiklopedia Dunia