2009-03-18 9 views
48

gui 툴킷으로 작업 한 적이 있다면, 모든 것이 완료된 후에 실행해야하는 이벤트 루프/메인 루프가 있으며, 이는 애플리케이션을 계속 유지하면서 다른 이벤트에 응답 할 수 있음을 알고 있습니다. 예를 들어, Qt를 들어, 당신이 이런 짓을 했을까 주() :이 경우, app.exec()는 응용 프로그램의 주 루프입니다 기본 이벤트 루프는 어떻게 구현합니까?

int main() { 
    QApplication app(argc, argv); 
    // init code 
    return app.exec(); 
} 

.

확실한 방법

는 것이다 루프 이런 종류의 구현 :

void exec() { 
    while (1) { 
     process_events(); // create a thread for each new event (possibly?) 
    } 
} 

그러나 이것은 100 %로 CPU 모자와 practicaly 쓸모가 없다. 자, 어떻게 CPU를 모두 먹지 않고 반응하는 이벤트 루프를 구현할 수 있습니까?

답변은 Python 및/또는 C++에서 높이 평가됩니다. 감사.

각주 : 학습을 위해 자체 시그널/슬롯을 구현하고이를 사용하여 맞춤 이벤트 (예 : go_forward_event(steps))를 생성합니다. 그러나 수동으로 시스템 이벤트를 사용할 수있는 방법을 알고 있다면 그 사실을 알고 싶습니다.

+3

난 당신이 Qt의 소스 코드에 탐구와()가 간부로 일을 정확하게 볼 수 있다고 생각합니다. 아마 당신에게 좋은 지적을 줄 것입니다. – JimDaniel

답변

62

나는 같은 것에 대해 많이 궁금해하곤했다!

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>은 UNIX의 경우 ::fd_set이며 "whatHappened"는 실제로 FD_ISSET을 통해 쿼리됩니다. 실제 대기 핸들은 다양한 방법으로 획득됩니다. 예를 들어 m_xConnection은 :: XConnectionNumber()에서 가져올 수 있습니다. X11은 또한 - :: XNextEvent()를 위해 높은 수준의 이식 가능한 API를 제공하지만, 이것을 사용한다면 여러 이벤트 소스를 동시에 기다릴 수 없습니다. .

차단은 어떻게 작동합니까? "waitOnAll"은 OS에게 프로세스를 "휴면 목록"에 넣도록 지시하는 syscall입니다. 즉, 대기 중 하나에서 이벤트가 발생할 때까지 CPU 시간이 제공되지 않습니다. 그러면 프로세스가 유휴 상태이고 0 % CPU를 사용한다는 의미입니다. 이벤트가 발생하면 프로세스가 프로세스를 잠시 응답 한 다음 유휴 상태로 돌아갑니다.GUI 응용 프로그램은 거의 모두 시간을 유휴로 소비합니다.

자고있는 동안 모든 CPU주기는 어떻게됩니까? 달려있어. 때로는 다른 프로세스가이를 사용하게됩니다. 그렇지 않은 경우 운영 체제는 CPU를 사용 중이거나 일시적인 저전력 모드로 전환합니다.

자세한 내용을 문의하십시오!

+0

시스템 신호가 아닌 대기 신호를 기다리는 대기 시스템을 어떻게 구현합니까? – fengshaun

+0

내가 말했듯이, 귀하의 코드는 이벤트에 대한 반응으로 만 실행됩니다. 따라서 자신의 이벤트를 실행하면 일부 시스템 이벤트에 대한 대응으로이를 수행하게됩니다. 그러면 사용자 정의 이벤트에 대한 이벤트 시스템이 실제로 필요하지 않음이 분명 해집니다. 핸드러를 직접 호출하기 만하면됩니다! –

+0

예를 들어 "Button :: clicked"신호를 생각해보십시오. 시스템 이벤트 (왼쪽 마우스 버튼 해제)에 대한 응답으로 만 실행됩니다. 그래서 코드는 "가상 void Button :: handleLeftRelease (point) {clicked.invoke();}"가 될 것입니다. –

11

일반적으로 나는 counting semaphore의 일종으로 이런 짓을 했을까 :

  1. 세마포어는 0에서 시작.
  2. 이벤트 루프는 세마포어에서 대기합니다.
  3. 이벤트가 들어오고, 세마포어가 증가합니다.
  4. 이벤트 처리기는 해당 세마포어를 차단 해제 및 감소시키고 이벤트를 처리합니다.
  5. 모든 이벤트가 처리되면 세마포가 0이되고 이벤트 루프가 다시 차단됩니다.

복잡하지 않으려면 while 루프에 sleep() 호출을 추가하고 잠자기 시간은 짧아야합니다. 그러면 메시지 처리 스레드가 다른 스레드에 대한 CPU 시간을 갖게됩니다. CPU가 더 이상 100 % 고정되지는 않지만 여전히 낭비입니다.

+0

이것은 유혹을 불러들입니다. 스레딩에 대해 더 배워야 할 것입니다. 감사. – fengshaun

+0

그래서 우리는 바쁜 기다림을하는 또 다른 스레드가 필요합니까? –

+0

@FallingFromBed - 바쁜 대기는 아니지만 sepmaphore에서 차단 대기. 블로킹 대기로 인해 CPU 시간이 소모되지 않으므로 그 차이가 중요합니다. –

10

간단하고 가벼운 메시징 라이브러리 인 ZeroMQ (http://www.zeromq.org/)를 사용합니다. 오픈 소스 라이브러리 (LGPL)입니다. 이것은 아주 작은 도서관입니다. 내 서버에서 전체 프로젝트가 약 60 초 만에 컴파일됩니다.

ZeroMQ는 이벤트 중심 코드를 크게 단순화 할뿐만 아니라 성능면에서 가장 효율적인 솔루션입니다. ZeroMQ를 사용하여 스레드 간 통신은 세마포 또는 로컬 UNIX 소켓을 사용하는 것보다 훨씬 빠릅니다 (속도면에서). ZeroMQ는 100 % 휴대용 솔루션이 될 수 있지만 다른 모든 솔루션은 코드를 특정 운영 체제에 연결합니다.

+3

와우, 멋지다. –

21

파이썬 :

당신은 아마 파이썬의 이벤트 루프에 대한 최선의 구현입니다 Twisted reactor의 구현을 볼 수 있습니다. Twisted의 Reactors는 인터페이스의 구현이며 select, epoll, kqueue (이러한 시스템 호출을 사용하는 API를 기반으로 함), QT 및 GTK 툴킷을 기반으로하는 반응기가 있습니다.

간단한 구현은 선택 사용하는 것입니다 :

#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() 
관련 문제