如果你使用过gui工具包,你知道有一个event-loop/main-loop应该在所有事情完成后执行,它将保持应用程序活跃并响应不同的事件。例如,对于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)
)。但如果你知道我如何手动使用系统事件,我也想知道这一点。
7条答案
按热度按时间tf7tbtn21#
我以前也经常想知道!
GUI主循环看起来像这样,在伪代码中:
什么是“Waitable”?嗯,它依赖于系统。在UNIX上,它被称为“文件描述符”,而“waitOnAll”是::select系统调用。所谓的
vector<Waitable>
在UNIX上是::fd_set
,而“whatHappened”实际上是通过FD_ISSET
查询的。实际的等待句柄可以通过各种方式获取,例如m_xConnection
可以从::XConnectionNumber()获取。X11还为此提供了一个高级的可移植API--::XNextEvent()--但如果要使用它,您将无法 * 同时 * 等待多个事件源。阻塞是如何工作的?“waitOnAll”是一个系统调用,它告诉操作系统将您的进程置于“睡眠列表”中。这意味着在其中一个等待对象上发生事件之前,您不会获得任何CPU时间。这意味着您的进程处于空闲状态,消耗0%的CPU。当事件发生时,你的进程会对它做出短暂的React,然后返回到空闲状态。GUI应用程序几乎所有的时间都在空闲状态。
当你睡眠时,所有的CPU周期会发生什么?取决于。有时另一个进程会使用它们。如果没有,你的操作系统将使CPU忙循环,或者将其置于临时低功耗模式等。
详情请咨询!
h43kikqp2#
Python:
你可以看看Twisted reactor的实现,这可能是python中事件循环的最佳实现。Twisted中的Reactors是接口的实现,你可以指定一个类型的reactors来运行:select、epoll、kqueue(都基于使用这些系统调用的c API),还有基于QT和GTK工具包的React器。
一个简单的实现是使用select:
zte4gxcn3#
一般来说,我会用某种类型的counting semaphore来做这件事:
1.信号量从零开始。
1.事件循环等待信号量。
1.事件进入,信号量递增。
1.事件处理程序解除信号量阻塞并使其递减,然后处理事件。
1.处理完所有事件后,信号量为零,事件循环再次阻塞。
如果你不想变得那么复杂,你可以在while循环中添加一个sleep()调用,它的sleep时间很小。这将导致你的消息处理线程将它的CPU时间让给其他线程。CPU不再是100%,但它仍然是相当浪费的。
flseospp4#
我将使用一个简单的轻量级消息传递库ZeroMQ(http://www.zeromq.org/),它是一个开源库(LGPL)。在我的服务器上,整个项目的编译时间大约是60秒。
ZeroMQ将极大地简化您的事件驱动代码,并且它也是性能方面最有效的解决方案。使用ZeroMQ在线程之间进行通信(就速度而言)比使用信号量或本地UNIX套接字快得多。ZeroMQ也是一个100%可移植的解决方案,而所有其他解决方案都将您的代码绑定到特定的操作系统。
p8h8hvxi5#
下面是一个C++事件循环。在创建对象
EventLoop
时,它创建了一个线程,该线程持续运行给定的任何任务。如果没有可用的任务,主线程将进入睡眠状态,直到添加一些任务。首先,我们需要一个线程安全队列,它允许多个生产者和至少一个消费者(
EventLoop
线程)。EventLoop
对象控制消费者和生产者。稍加修改,它可以添加多个消费者(runners线程),而不是只有一个线程。打印当前时间戳的实用工具:
示例程序使用这些:
输出测试示例:
更新
最后,事件循环呈现的是一个像时间管理器.一个更好的时间管理器的接口将不强制用户使用线程.这将是一个例子:
wpcxdonn6#
这个答案适用于类Unix系统,如Linux或Mac OS X。我不知道在Windows中如何做到这一点。
select()或pselect()。Linux也有poll()。
查看手册页以获得深入的详细信息。此系统调用需要文件描述符列表、超时和/或信号掩码。此系统调用让程序等待事件发生。如果列表中的一个文件描述符准备好读取或写入,(取决于设置,参见手册页),超时到期或信号到达,这个系统调用将返回。然后程序可以读/写文件描述符,处理信号或做其他事情。之后它再次调用(p)select/poll并等待下一个事件。
套接字应该以非阻塞的方式打开,以便在没有数据/缓冲区满时读取/写入功能返回。对于通用显示服务器X11,GUI通过套接字处理,并具有文件描述符。因此可以以相同的方式处理。
r3i60tvu7#
在python中创建事件循环的基本应用之前。让我们了解一下
what is Event Loop ?
事件循环是任何异步I/O框架的核心组件,它允许您并发执行I/O操作,而不会阻塞程序的执行。事件循环在单个线程中运行,并负责在I/O事件发生时接收和调度I/O事件[如阅读/写入文件或键盘中断]。