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)
)。しかし、システムイベントを手動で使用する方法を知っている場合は、それについても知りたいと思います。
私は同じことについて多くを疑問に思っていました!
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をビジーループにするか、一時的な低電力モードなどに設定します。
詳細についてはお問い合わせください!
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()
一般的に、私は セマフォを数える :
それほど複雑にしたくない場合は、ごくわずかな短いスリープ時間でwhileループにsleep()呼び出しを追加することができます。これにより、メッセージ処理スレッドは他のスレッドにCPU時間をもたらします。 CPUはもう100%に固定されませんが、それでもかなり無駄です。
ZeroMQ( http://www.zeromq.org/ )というシンプルで軽量なメッセージングライブラリを使用します。これはオープンソースライブラリ(LGPL)です。これは非常に小さなライブラリです。私のサーバーでは、プロジェクト全体が約60秒でコンパイルされます。
ZeroMQは、イベント駆動型コードを大幅に簡素化し、パフォーマンスの面でも最も効率的なソリューションです。 ZeroMQを使用したスレッド間の通信は、セマフォまたはローカルUNIXソケットを使用するよりもはるかに高速です(速度の点で)。 ZeroMQは100%ポータブルなソリューションでもありますが、他のすべてのソリューションはコードを特定のオペレーティングシステムに結び付けます。