如果你使用过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(;;) { vectorwaitables; 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; } } }
什么是"等待"?嗯,这取决于系统.在UNIX上,它被称为"文件描述符","waitOnAll"是:: select系统调用.所谓的vector
是::fd_set
在UNIX上,实际上是通过查询"whatHappened" FD_ISSET
.实际的waitable-handle以各种方式获取,例如m_xConnection
可以从:: XConnectionNumber()获取.X11还提供了一个高层次的,可移植的API为这一点- :: XNextEvent例行() -但如果你使用,你将无法等待几个事件源同时.
阻止如何工作?"waitOnAll"是一个系统调用,告诉操作系统将您的进程置于"睡眠列表"中.这意味着在其中一个等待事件发生之前,您不会获得任何CPU时间.这意味着您的进程处于空闲状态,消耗0%的CPU.当事件发生时,您的进程将对其作出短暂反应,然后返回空闲状态.GUI应用程序几乎把所有时间花在闲置上.
你睡觉时所有CPU周期会发生什么?要看.有时另一个过程会对它们有用.如果没有,您的操作系统将忙于循环CPU,或将其置于临时低功耗模式等.
请询问更多详情!
蟒蛇:
您可以查看Twisted reactor的实现,这可能是python中事件循环的最佳实现.Twisted中的反应器是接口的实现,您可以指定要运行的类型反应器:select,epoll,kqueue(所有基于使用这些系统调用的ac api),还有基于QT和GTK工具包的反应器.
一个简单的实现是使用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%可移植的解决方案,而所有其他解决方案将您的代码绑定到特定的操作系统.