イベントループは、ポーリングが最適化されたfor / whileループですか?


55

イベントループとは何かを理解しようとしています。多くの場合、イベントループでは、イベントが発生したことが通知されるまで何かをするという説明があります。その後、イベントを処理し、以前の操作を続行します。

上記の定義を例にマッピングします。イベントループで「リッスン」するサーバーがあり、ソケット接続が検出されると、そこからのデータが読み取られて表示され、その後サーバーは以前と同じようにリッスンを再開/開始します。


ただし、このイベントが発生し、「そのような」通知を受け取ることは、私にとって非常に重要です。あなたは言うことができる:「それはあなたがイベントリスナーを登録しなければならない「ちょうどそのような」ではない」しかし、イベントリスナーとは何か、何らかの理由で返されない関数です。イベントが発生したときに通知されるのを待っている独自のループですか?イベントリスナーもイベントリスナーを登録する必要がありますか?どこで終わりますか?


イベントは作業するのに最適な抽象概念ですが、単なる抽象概念です。結局、ポーリングは避けられないと思います。おそらく私たちはコードでそれをしていませんが、下位レベル(プログラミング言語の実装またはOS)が私たちのためにそれをしています。

基本的には、次の擬似コードになります。このコードは、十分に低い場所で実行されているため、ビジー待機を発生させません。

while(True):
    do stuff
    check if event has happened (poll)
    do other stuff

これはアイデア全体に対する私の理解であり、これが正しいかどうか聞きたいと思います。私は全体の考えが根本的に間違っていることを受け入れることに心を開いています。その場合、正しい説明が欲しいです。


3
イベントシステムはObserverパターンの実装です。パターンを理解することは、イベントの理解を強固にするはずです。ポーリングは不要です。
スティーブンエバーズ

1
たとえ言語の構造が抽象化しても、最終的にははい。
GrandmasterB

@SteveEvers wikiリンク。EventSourceキーボード入力をポーリングしない場合はどうしますか?
TheMeaningfulEngineer

@Alan:それは何でもできますが、キーボード入力の場合、特にアプリケーションをキーボードイベントをリッスンするように登録するAPIが存在します。これをポーリングなしでイベントソースとして使用できます。もちろん、十分に下がった場合、USBは常にポーリングを行いますが、割り込み駆動型のPS / 2キーボードを使用していると仮定し、ポーリングなしのイベントに基づくキーボード入力スタックがあるとします。
-Phoshi

私は長年からこの質問をしてきましたが、なぜ私がそれを尋ねることを決して気にしなかったのか私にはわかりません。おかげで、@ Karl Bielefeldtが私に教えてくれた理解に満足しています。
0xc0de

回答:


54

イベントの準備ができていない場合、ほとんどのイベントループは一時停止します。つまり、オペレーティングシステムは、イベントが発生するまでタスクに実行時間を与えません。

イベントが押されているキーだとしましょう。オペレーティングシステムのどこかにキー入力をチェックするループがあるかどうかを尋ねるかもしれません。答えはノーです。キーが押されると割り込みが生成され、ハードウェアによって非同期に処理されます。同様に、タイマー、マウスの動き、パケットの到着など。

実際、ほとんどのオペレーティングシステムでは、イベントのポーリングが抽象化されています。ハードウェアとOSはイベントを非同期的に処理し、アプリケーションがポーリングできるキューに入れます。組み込みシステムのハードウェアレベルで真のポーリングのみを実際に見ることができます。


4
割り込みはワイヤの電圧の変化ではありませんか?それ自体でイベントをトリガーできますか、または電圧値のピンをポーリングする必要がありますか?
TheMeaningfulEngineer

8
それ自体でイベントをトリガーできます。プロセッサはそのように設計されています。実際、プロセッサは割り込みによってスリープから復帰することができます。
カールビーレフェルト

2
声明と混同されていMost event loops will blockます。これは、「スレッドを使用するのではなく、イベントブロッキングパラダイムがノンブロッキング非同期呼び出しを使用する」にどのように適合しますか?
TheMeaningfulEngineer

1
GTK +のようなライブラリのイベントループを見ると、それらは新しいイベントをチェックしてからループでイベントハンドラーを呼び出しますが、イベントがない場合は、セマフォまたはタイマーなどでブロックします。個々の開発者は、空のイベントキューでブロックしない独自のイベントループを作成しますが、広く使用されているライブラリはすべてブロックします。それ以外の場合、イベントループは非効率的です。
カールビーレフェルト

1
あなたはそのように見ることができると思いますが、それはアプリケーションコードではなくライブラリやOSでのマルチスレッドです。イベントループは、アプリケーション開発者にとって同期の問題を取り除きます。
カールビーレフェルト

13

イベントリスナーは、独自のループを実行する関数としてではなく、最初のランナーが発砲を待っているリレーレースとして考えています。ポーリングの代わりにイベントを使用する重要な理由は、CPUサイクルの方がイベントがより効率的であることです。どうして?(ソースコードをダウンするのではなく)ハードウェアを上から見てください。

Webサーバーを検討してください。サーバーが呼び出しlisten()てブロックすると、コードがリレーランナーとしての役割を果たします。新しい接続の最初のパケットが到着すると、ネットワークカードはオペレーティングシステムを中断してレースを開始します。OSは、パケットを取得する割り込みサービスルーチン(ISR)を実行します。ISRは、バトンを接続を確立する上位レベルのルーチンに渡します。接続が有効になると、そのルーチンはバトンをlisten()に渡し、バトンはコードに渡されます。その時点で、接続で必要なことを実行できます。私たちが知っている限りでは、レースの間、各リレーランナーはパブに行くことができます。イベント抽象化の強みは、コードが知ったり気にしたりする必要がないことです。

一部のオペレーティングシステムには、レースの一部を実行し、バトンを渡した後、開始点にループバックして次のレースが始まるのを待つイベント処理コードが含まれています。その意味で、イベント処理は多くの同時ループでの最適化されたポーリングです。ただし、プロセスを開始する外部トリガーが常にあります。イベントリスナーは、返されない関数ではなく、実行前にその外部トリガーを待機している関数です。のではなく:

while(True):
    do stuff
    check if event has happened (poll)
    do other stuff

私はこれを次のように考えます:

on(some event):    //I got the baton
     do stuff
     signal the next level up    //Pass the baton

そしてsignal、ハンドラーが次回実行される間、概念的にはコードの実行やループはありません。


これは理にかなっていますが、割り込みを待っている間にイベントループは何をしますか?ブロックしている場合、それはイベントループの反対のパラダイムである「マルチスレッド」アプローチではありませんか?
TheMeaningfulEngineer

コードにループがある場合、ループforever: { select(); do stuff; }を通過するたびに再びレースに参加します。単一のスレッドから繰り返し実行するか、別々のスレッドまたはプロセッサで並行して実行するかに関係なく、各イベントはそれぞれのレースと考えています。たとえば、Webブラウザーは、少なくとも1つはUI用で、もう1つはダウンロードする各ページに1つ、別々のスレッドで複数のイベントループを持つマルチスレッドプログラムです。コーディング時に尋ねる質問は、「イベントを十分に速く処理するにはどうすればよいですか」です。答えはループである場合もあれば、スレッドである場合もあり、組み合わせである場合もあります。
cxw

私は20年以上にわたってイベントベースのプログラミングを行ってきましたが、この答えは非常に紛らわしいと感じました。リスナーが機能するには、イベントを待機し、登録されたすべてのリスナーにルーティングするループが必要です。(ハードウェア割り込みではなくソフトウェアについて話していると仮定)
ブライアンオークリー

8

いいえ。「最適化されたポーリング」ではありません。イベントループは、ポーリングの代わりに割り込み駆動型のI / Oを使用します。

while、until、forなどのループはポーリングループです。

「ポーリング」とは、何かを繰り返しチェックするプロセスです。ループコードは連続して実行されるため、また小さな「タイトな」ループであるため、プロセッサがタスクを切り替えて他のことを行う時間はほとんどありません。ほとんどすべての「ハング」、「フリーズ」、「ロックアップ」、またはコンピューターが応答しなくなったときに呼び出すものはすべて、コードが意図しないポーリングループでスタックしていることを表しています。インストルメンテーションは100%のCPU使用率を示します。

割り込み駆動型のイベントループは、ポーリングループよりもはるかに効率的です。ポーリングはCPUサイクルの非常に無駄な使用であるため、それを排除または最小化するためにあらゆる努力が払われています。

ただし、コード品質を最適化するために、ほとんどの言語は、プログラム内で機能的に同様の目的を果たすため、イベント処理コマンドに対してポーリングループパラダイムを可能な限り使用しようとします。したがって、ポーリングはキー入力などを待つためのより馴染みのある方法であるため、経験の浅い人はそれを使用して、それ自体で正常に実行されるプログラムを簡単に作成できますが、実行中は他に何も機能しません。マシンを「乗っ取り」ました。

他の回答で説明したように、割り込み駆動型のイベント処理では、基本的に「フラグ」がCPU内に設定され、そのフラグが他のプロセス(キーボードなど)によって変更されるまで、プロセスは「一時停止」(実行できません)ユーザーがキーを押したときにドライバーが変更します)。フラグが実際のハードウェア状態である場合(たとえば、ラインが「ハイに引き上げられる」)、「割り込み」または「ハードウェア割り込み」と呼ばれます。ただし、ほとんどは、CPUまたはメインメモリ(RAM)のメモリアドレスとして実装され、「セマフォ」と呼ばれます。

セマフォはソフトウェアの制御下で変更できるため、ソフトウェアプロセス間で非常に高速でシンプルな信号メカニズムを提供できます。

ただし、割り込みはハードウェアによってのみ変更できます。割り込みの最も一般的な使用方法は、内部クロックチップによって定期的にトリガーされるものです。クロック割り込みによって起動される無数の種類のソフトウェアアクションの1つは、セマフォの変更です。

私は多くのことを省きましたが、どこかで止めなければなりませんでした。詳細が必要な場合はお問い合わせください。


7

通常、答えはハードウェア、あなたが制御しないOSとバックグラウンドスレッドが共謀して、見た目を楽にすることです。ネットワークカードは、CPUに知らせるために割り込みを発生させるデータを受信します。OSの割り込みハンドラーが処理します。次に、制御しないバックグラウンドスレッド(イベントに登録して作成され、イベントに登録してからスリープ状態になっているスレッド)が、イベント処理の一部としてOSによってウェイクアップされ、イベントハンドラーを実行します。


7

これまで見てきた他のすべての答えに反して、「はい」と言います。他の答えは物事を複雑にしすぎていると思います。概念的な観点から見ると、すべてのイベントループは本質的に次のとおりです。

while <the_program_is_running> {
    event=wait_for_next_event()
    process_event(event)
}

イベントループを初めて理解しようとしている場合、それらを単純なループと考えても害はありません。基盤となるフレームワークは、OSがイベントを配信するのを待ってから、イベントを1つ以上のハンドラーにルーティングし、次のイベントを待機する、などです。アプリケーションソフトウェアの観点からは、これですべてです。


1
わかりやすくするために+1。しかし、重点は「待機」にあります。このコードの実行はループの最初の行で停止し、イベントが発生するまで継続しません。これは、イベントが発生するまで繰り返しイベントをチェックするビジーポーリングループとは異なります。
トムパニング

1

すべてのイベントトリガーがループで処理されるわけではありません。私がよく自分のイベントエンジンを書く方法は次のようになります:

interface Listener {
    void handle (EventInfo info);
}

List<Listener> registeredListeners

void triggerEvent (EventInfo info) {
    foreach (listener in registeredListeners) { // Memo 1
        listener.handle(info) // the handling may or may not be synchronous... your choice
    }
}

void somethingThatTriggersAnEvent () {
    blah
    blah
    blah
    triggerEvent(someGeneratedEventInfo)
    more blah
}

メモ1はループ上にありますが、ループは各リスナーに通知するためのものです。イベントトリガー自体は、必ずしもループ内にあるとは限りません。

理論的には、OSが何らかのregisterListenerAPIを公開している場合、OSレベルのキーイベントは同じ手法を使用できます(代わりにポーリングを行うことが多いと思いますか?ここで推測しています)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.