Как бы вы реализовали базовый цикл событий?

Если вы работали с инструментами 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?) } } 

Но это закрывает процессор до 100% и практически бесполезно. Теперь, как я могу реализовать такой цикл событий, который реагирует, не потребляя процессор вообще?

Ответы приветствуются в Python и / или C ++. Благодарю.

Сноска: Для обучения я буду реализовывать свои собственные сигналы / слоты, и я бы использовал их для создания пользовательских событий (например, go_forward_event(steps) ). Но если вы знаете, как я могу использовать системные события вручную, я хотел бы узнать об этом.

  • Выбор встроенного языка
  • Строительство Mesa для окон 7. Mesa 9.1
  • Cython & C ++: передача по ссылке
  • Имеет ли C # что-нибудь сопоставимое с пониманием списка Python?
  • Как загрузить C DLL из SXS в Python?
  • Могу ли я создать «представление» в списке Python?
  • 4 Solutions collect form web for “Как бы вы реализовали базовый цикл событий?”

    Я часто задавался вопросом о том же!

    Основной цикл 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; } } } 

    Что такое «Ожидание»? Ну, это зависит от системы. В UNIX это называется «файловым дескриптором», а «waitOnAll» – системным вызовом :: select. Так называемый vector<Waitable> представляет собой ::fd_set в UNIX, и «whatHappened» на самом деле запрашивается через FD_ISSET . Фактические обработчики получаются по-разному, например m_xConnection можно взять из :: XConnectionNumber (). X11 также предоставляет высокоуровневый переносимый API для этого – :: XNextEvent (), но если вы будете использовать его, вы не сможете ждать нескольких источников событий одновременно .

    Как работает блокировка? «waitOnAll» – это системный вызов, который сообщает ОС о том, что ваш процесс переходит в «список сна». Это означает, что вам не дано никакого процессорного времени, пока событие не произойдет на одной из ожидающих. Это значит, что ваш процесс простаивает, потребляя 0% CPU. Когда произойдет событие, ваш процесс ненадолго отреагирует на него, а затем вернется в состояние ожидания. Приложения GUI тратят почти все свое время на холостом ходу.

    Что происходит со всеми циклами процессора во время сна? Зависит. Иногда другой процесс будет полезен для них. Если нет, ваша ОС будет занята циклом процессора или переведет его во временный режим с низким энергопотреблением и т. Д.

    Пожалуйста, запросите дополнительную информацию!

    Python:

    Вы можете взглянуть на реализацию реактора Twisted, который, вероятно, является лучшей реализацией цикла событий на питоне. Реакторы в 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() 

    Вообще-то я бы сделал это с помощью своего рода счетного семафора :

    1. Семафор начинается с нуля.
    2. Контур события ожидает семафора.
    3. Приходят события (ы), семафор увеличивается.
    4. Обработчик событий разблокирует и уменьшает семафор и обрабатывает событие.
    5. Когда все события обрабатываются, семафор равен нулю, а цикл цикла событий снова.

    Если вы не хотите усложнять ситуацию, вы можете просто добавить вызов sleep () в цикл while с минимально низким временем сна. Это приведет к тому, что поток обработки сообщений даст процессорное время другим потокам. ЦП не будет привязан на 100% больше, но он все еще довольно расточительный.

    Я бы использовал простую, легковесную библиотеку сообщений ZeroMQ ( http://www.zeromq.org/ ). Это библиотека с открытым исходным кодом (LGPL). Это очень маленькая библиотека; на моем сервере весь проект составляет около 60 секунд.

    ZeroMQ значительно упростит ваш управляемый событиями код, и это также самое эффективное решение с точки зрения производительности. Коммуникация между потоками с использованием ZeroMQ намного быстрее (с точки зрения скорости), чем использование семафоров или локальных UNIX-сокетов. ZeroMQ также является портативным решением на 100%, тогда как все остальные решения привязывают ваш код к конкретной операционной системе.

    Python - лучший язык программирования в мире.