web-dev-qa-db-ja.com

基本的なイベントループをどのように実装しますか?

GUIツールキットを使用したことがある場合は、すべてが完了した後に実行されるイベントループ/メインループがあり、アプリケーションをさまざまなイベントに対応した状態に保ちます。たとえば、Qtの場合、main()でこれを行います。

_int main() {
    QApplication app(argc, argv);
    // init code
    return app.exec();
}
_

この場合、app.exec()はアプリケーションのメインループです。

この種のループを実装する明白な方法は次のとおりです。

_void exec() {
    while (1) {
        process_events(); // create a thread for each new event (possibly?)
    }
}
_

しかし、これはCPUを100%に制限し、実際には役に立たない。では、CPUを完全に消費することなく応答するようなイベントループをどのように実装できますか?

答えはPythonおよび/またはC++。ありがとう。

脚注:学習のために、独自のシグナル/スロットを実装し、それらを使用してカスタムイベントを生成します(例:go_forward_event(steps))。しかし、システムイベントを手動で使用する方法を知っている場合は、それについても知りたいと思います。

54
fengshaun

私は同じことについて多くを疑問に思っていました!

GUIのメインループは、擬似コードでは次のようになります。

void App::exec() {
    for(;;) {
        vector<Waitable> waitables;
        waitables.Push_back(m_networkSocket);
        waitables.Push_back(m_xConnection);
        waitables.Push_back(m_globalTimer);
        Waitable* whatHappened = System::waitOnAll(waitables);
        switch(whatHappened) {
            case &m_networkSocket: readAndDispatchNetworkEvent(); break;
            case &m_xConnection: readAndDispatchGuiEvent(); break;
            case &m_globalTimer: readAndDispatchTimerEvent(); break;
        }
    }
}

「Waitable」とは何ですか?まあ、それはシステムに依存しています。 UNIXでは、「ファイル記述子」と呼ばれ、「waitOnAll」は:: selectシステムコールです。いわゆるvector<Waitable>::fd_set UNIXの場合、「whatHappened」は実際にFD_ISSET。実際の待機可能ハンドルは、さまざまな方法で取得されます。たとえば、m_xConnectionは:: XConnectionNumber()から取得できます。 X11は、このための高レベルでポータブルなAPIも提供します-:: XNextEvent()-しかし、それを使用する場合、いくつかのイベントソースを待つことはできません同時に

ブロッキングはどのように機能しますか? 「waitOnAll」は、プロセスを「スリープリスト」に入れるようOSに指示するsyscallです。つまり、いずれかのwaitableでイベントが発生するまで、CPU時間は与えられません。これは、プロセスがアイドル状態であり、CPUを0%消費していることを意味します。イベントが発生すると、プロセスは一時的にそれに反応してからアイドル状態に戻ります。 GUIアプリはほとんどallのアイドリング時間を費やします。

寝ている間、すべてのCPUサイクルはどうなりますか?依存します。別のプロセスがそれらを使用する場合があります。そうでない場合、OSはCPUをビジーループにするか、一時的な低電力モードなどに設定します。

詳細についてはお問い合わせください!

70
Iraimbilanja

Python:

Twisted reactor の実装を見ることができます。これはおそらくPythonのイベントループの最適な実装です。 TwistedのReactorはインターフェースの実装であり、実行するタイプReactorを指定できます:select、epoll、kqueue(すべてこれらのシステムコールを使用するC APIに基づく)、QTおよびGTKツールキットに基づくReactorもあります。

単純な実装では、selectを使用します。

#echo server that accepts multiple client connections without forking threads

import select
import socket
import sys

Host = ''
port = 50000
backlog = 5
size = 1024
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((Host,port))
server.listen(backlog)
input = [server,sys.stdin]
running = 1

#the eventloop running
while running:
    inputready,outputready,exceptready = select.select(input,[],[])

    for s in inputready:

        if s == server:
            # handle the server socket
            client, address = server.accept()
            input.append(client)

        Elif s == sys.stdin:
            # handle standard input
            junk = sys.stdin.readline()
            running = 0

        else:
            # handle all other sockets
            data = s.recv(size)
            if data:
                s.send(data)
            else:
                s.close()
                input.remove(s)
server.close() 
21
Vasil

一般的に、私は セマフォを数える

  1. セマフォはゼロから始まります。
  2. イベントループはセマフォで待機します。
  3. イベントが入ると、セマフォがインクリメントされます。
  4. イベントハンドラは、セマフォのブロックを解除してデクリメントし、イベントを処理します。
  5. すべてのイベントが処理されると、セマフォはゼロになり、イベントループは再びブロックされます。

それほど複雑にしたくない場合は、ごくわずかな短いスリープ時間でwhileループにsleep()呼び出しを追加することができます。これにより、メッセージ処理スレッドは他のスレッドにCPU時間をもたらします。 CPUはもう100%に固定されませんが、それでもかなり無駄です。

11
Eric Petroelje

ZeroMQ( http://www.zeromq.org/ )というシンプルで軽量なメッセージングライブラリを使用します。これはオープンソースライブラリ(LGPL)です。これは非常に小さなライブラリです。私のサーバーでは、プロジェクト全体が約60秒でコンパイルされます。

ZeroMQは、イベント駆動型コードを大幅に簡素化し、パフォーマンスの面でも最も効率的なソリューションです。 ZeroMQを使用したスレッド間の通信は、セマフォまたはローカルUNIXソケットを使用するよりもはるかに高速です(速度の点で)。 ZeroMQは100%ポータブルなソリューションでもありますが、他のすべてのソリューションはコードを特定のオペレーティングシステムに結び付けます。

10
Anonymous