当前位置:  开发笔记 > 编程语言 > 正文

你将如何实现一个基本的事件循环?

如何解决《你将如何实现一个基本的事件循环?》经验,为你挑选了4个好方法。

如果你使用过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)).但是如果你知道如何手动使用系统事件,我也想知道这一点.



1> 小智..:

我曾经多次想到这一点!

GUI主循环在伪代码中如下所示:

void App::exec() {
    for(;;) {
        vector 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;
        }
    }
}

什么是"等待"?嗯,这取决于系统.在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,或将其置于临时低功耗模式等.

请询问更多详情!



2> Vasil..:

蟒蛇:

您可以查看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() 



3> Eric Petroel..:

一般来说,我会用某种计数信号量来做到这一点:

    信号量从零开始.

    事件循环等待信号量.

    事件进来,信号量增加.

    事件处理程序取消阻塞和减少信号量并处理事件.

    处理完所有事件后,信号量为零,事件循环再次阻塞.

如果你不想那么复杂,你可以在你的while循环中添加一个sleep()调用,并且休眠时间很短.这将导致您的消息处理线程将其CPU时间转移到其他线程.CPU不会再以100%挂钩,但它仍然相当浪费.



4> 小智..:

我会使用一个简单,轻量级的消息库,名为ZeroMQ(http://www.zeromq.org/).它是一个开源库(LGPL).这是一个非常小的图书馆; 在我的服务器上,整个项目在大约60秒内编译.

ZeroMQ将极大地简化您的事件驱动代码,并且它在性能方面也是最有效的解决方案.使用ZeroMQ在线程之间进行通信比使用信号量或本地UNIX套接字快得多(就速度而言).ZeroMQ也是100%可移植的解决方案,而所有其他解决方案将您的代码绑定到特定的操作系统.

推荐阅读
跟我搞对象吧
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有