2017-01-03 7 views
0

다음과 같은 방법으로 버클리 소켓 선택 기능을 사용하고 있습니다.버클리 소켓에서 스레드 알림 사용

/*Windows and linux typedefs/aliases/includes are made here with wsa 
junk already taken care of.*/ 

/**Check if a socket can receive data without waiting. 
\param socket The os level socket to check. 
\param to The timeout value. A nullptr value will block forever, and zero 
for each member of the value will cause it to return immediately. 
\return True if recv can be called on the socket without blocking.*/ 
bool CanReceive(OSSocket& socket, 
    const timeval * to) 
{ 
    fd_set set = {}; 
    FD_SET(socket, &set); 
    timeval* toCopy = nullptr; 
    if (to) 
    { 
     toCopy = new timeval; 
     *toCopy = *to; 
    } 

    int error = select((int)socket, &set, 0, 0, toCopy); 
    delete toCopy; 
    if (error == -1) 
     throw Err(); //will auto set from errno. 
    else if (error == 0) 
     return false; 
    else 
     return true; 
} 

나는 (aother 클래스에 싸서) 소켓의 컨테이너를보고 소켓에 액세스 할 준비가 무엇인지에 대한 정보를 저장하는 별도의 용기에 ID를 추가하는 클래스를 작성했습니다. 지도는 unordered_map입니다.

while(m_running) 
{ 
    for(auto& e : m_idMap) 
    { 
      auto id = e.first; 
      auto socket = e.second; 
      timeval timeout = ZeroTime; /*0sec, 0micro*/ 
      if(CanReceive(socket,&timeout) && 
       std::count(m_readyList.begin(),m_readyList.end(),socket) == 0) 
      { 
       /*only add sockets that are not on the list already.*/ 
       m_readyList.push_back(id); 
      } 
    } 
} 

내가 발견했는지 많은거야,이 코드는 미친 듯이 빠른 실행과 내일 (지도에서 하나의 소켓 40 %의 CPU 사용량)가없는 것처럼 CPU를 먹는다. 내 첫 번째 솔루션은 초당 반복을 설정된 값으로 유지하는 스마트 대기 함수를 사용하는 것이 었습니다. 그것은 어떤 사람들에게는 괜찮은 것처럼 보였다. 내 질문은 :이 방법을 사용하지 않고 소켓 준비가되었을 때 어떻게 알림을받을 수 있습니까? 비록 그것이 휴대 할 수 있도록 매크로 쓰레기가 필요할지라도 괜찮습니다. 운영 체제에서 나를 감시하고 소켓 준비가되었을 때 알림이나 이벤트를받을 수있는 방법이있을 수 있다고 생각할 수 있습니다. 그냥 분명히하기 위해, 나는 닷넷을 사용하지 않기로했다.

루프가 자체 스레드에서 실행되고 소켓 준비가 완료되면 소프트웨어의 다른 부분에 알림이 전송됩니다. 전체가 멀티 스레드이며이 부분을 제외한 모든 부분에서 이벤트 기반 알림 시스템을 사용하여 대기중인 대기 문제를 제거합니다. 나는이 것들이 OS에 의존하고이 영역에서 제한된다는 것을 이해한다.

편집 : 소켓은 BLOCKING 모드로 실행되지만 select에는 시간 제한이 없으므로 차단되지 않습니다. 그러나 전용 스레드에서 작동됩니다. 편집 : 시스템의 스마트 절전 기능이 뛰어나지 만 일부 알림 시스템 (예 : OS)에서 가능한만큼 좋은 성능을 발휘하지 못합니다.

+1

'recv'를 호출하지 않는 이유는 무엇입니까? 'recv'가 무엇을 할 것인지를 예측하기 위해'select'를 호출해야하는 이유는 무엇입니까? 그냥'recv'를 호출하고 무슨 일이 일어나는지 보지 않으시겠습니까? –

+0

또한이 작업을 수행하려면 실제로 라이브러리를 사용해야합니다. 100 % 제대로하는 것은 일의 * 많은 *이며, 발견 한 것처럼 최소한의 코드로 위조하는 것은 고통 스럽습니다. ASIO에는 강력한 기능이 있습니다. –

+0

David Schwartz : 소켓에 즉시 제공 할 항목이없는 경우 다른 소켓으로 이동하여 나에게 뭔가가 있는지 확인합니다. 수천 개의 소켓이 있습니다. recv를 호출하여 하나의 소켓을 차단하면 현재 준비가되어있는 다른 소켓은 무시됩니다. no-block 옵션을 사용하여 recv를 호출해도 대기 중으로 계속 바쁠 것이므로 아무 것도 해결하지 못합니다. – Matt

답변

1

먼저 소켓을 차단하지 않으려면 소켓을 비 블로킹 (non-blocking)으로 설정해야합니다. select 함수는 후속 작업이 차단되지 않는다는 보장을 제공하지 않습니다. 과거와 현재에 대해 알려주는 상태보고 기능입니다.

둘째, 가장 좋은 방법은 플랫폼에 따라 다릅니다. 많은 플랫폼 특정 코드를 작성하고 싶지 않으면 Boost ASIO 나 libevent와 같은 라이브러리를 사용해야합니다.

셋째, 모든 소켓에서 동시에 select으로 전화를 걸면 시간 초과가 발생합니다. 이 함수는 소켓 중 하나라도 읽을 수 있으면 즉시 반환하고, 그렇지 않은 경우에는 타임 아웃까지 대기합니다. select이 반환되면 타임 아웃되었거나 그렇지 않은 경우 읽을 수있는 소켓을보고합니다.

많은 수의 대기자 목록 때문에 단일 소켓을 읽을 수있게되면 곧바로 모든 프로세스에서 즉시 제거되어야하므로이 작업은 매우 열악합니다. 그러나 합리적인 휴대 성으로 최선을 다할 수 있습니다.

+0

select() 및 pselect()는 프로그램이 복수 파일 설명자를 모니터하여 하나 이상의 파일 설명자가 "ready"가 될 때까지 기다리는 것을 허용합니다 (예 : 입력 가능). 파일 디스크립터는 대응하는 I/O 동작 (예를 들어, 블로킹없이 read (2) 또는 충분히 작은 write (2)이면)을 수행 할 수 있다면 준비 상태로 간주된다. 보낸 사람 : http://man7.org/linux/man-pages/man2/select.2.html – Matt

+0

한 번에 여러 소켓을 호출하는 중 하나 이상의 소켓이 준비되었다고 말하면 개별적으로 확인해야합니다 준비가 완료되었습니다. 일부는 준비가되지 않았을 수 있습니다. – Matt

+2

'select' 함수는 전달할 파일 디스크립터 세트를 수정하여 어느 것이 준비 되었는 지 나타냅니다. 'select'가 히트를 리턴 할 때, 그것은 당신이'select'를 호출했을 때와 리턴했을 때 사이의 어느 시점에서 소켓 *이 준비되었음을 의미합니다. 그것은 미래의 준비 상태와 그것이 화상을 입었다는 가정을 한 사람들을 보장하지 않습니다. –

0

방법을 사용하지 않고 소켓 준비가 완료되면 어떻게 알림을받을 수 있습니까?

그게 바로 select()의 용도입니다. 아이디어는 select() 호출이 FD_SET()을 통해 전달 된 소켓 중 적어도 하나가 읽을 준비가 될 때까지 차단되어야한다는 것입니다. select()가 반환 된 후에는 FD_ISSET()을 호출하여 어느 소켓을 읽을 준비가되었는지 알 수 있고 소켓에서 recv()를 호출하여 소켓에서 데이터를 가져 와서 처리 할 수 ​​있습니다.그런 다음 다시 반복하고 select() 내에서 다시 잠자기 상태로 돌아가서 무한 반복을 반복합니다. 이러한 방식으로 가능한 한 빨리 모든 작업을 처리하면서 최소 CPU주기를 사용합니다.

전체 것은 멀티 스레드 (이 부분 제외) 그것의 모든 부분 바쁜 대기 문제를 제거하는 이벤트 기반 알림 시스템을 사용합니다.

주 스레드가 (선택의 내부 차단) 그리고 당신이 일어나 바로 일을 할 경우 (즉, 느리고 비효율적 타임 아웃에 의존하지 않고), 다음거야 그 해당 스레드에서 select()를 즉시 반환하도록하는 방법이 필요합니다. 필자가 경험하는 가장 확실한 방법은 pipe() 또는 socketpair()를 생성하고 스레드가 ready-for-read fd_set에 파일 설명자 쌍의 한쪽 끝을 포함하도록하는 것입니다. 그런 다음 다른 스레드가 해당 스레드를 깨우기를 원하면 쌍의 다른 끝에 바이트를 보내서 간단히 그렇게 할 수 있습니다. 그러면 select()가 돌아오고, 쓰레드는 싱글 바이트를 읽은 다음 (다시 던져 버림) 깨어 난 후에 무엇을 하든지 할 수 있습니다.

+0

타임 아웃을 0으로 설정했기 때문에 Select가 제 경우에는 차단되지 않습니다. 소켓이 준비 상태인지 아닌지 즉시 알려줍니다. 준비가되면 대기열로 간다. 그렇지 않으면 아무것도 발생하지 않는다. 파이프 또는 소켓 쌍에 대한 귀하의 아이디어는 좋은 아이디어이며 실제로 타임 아웃이 끝나기 전에 선택 호출을 중단하면 도움이 될 수있는 다른 코드가 있습니다. 고맙습니다. – Matt

+2

timeout 인수를 NULL로두면 무기한 차단됩니다 (또는 전달한 소켓 중 하나가 x에 대한 준비가 됨). CPU를 회전시키지 않으려면 적어도 하나의 소켓이 준비 될 때까지 블로킹이 필요합니다. –

+0

그 의견은 제가 함께 할 답변입니다.차단할 수 없기 때문에 (다른 소켓은 차단하는 동안 준비가되어있을 수도 있기 때문에) 적응 형 슬리핑 기능을 사용하여 초당 루프를 적절한 수준으로 유지할 수 밖에 없습니다 (루프는 10 마이크로 초당 최대 한 번 실행됩니다. CPU 사용량을 1 % 미만으로 유지). 나는 초당 천만 건의 수표가 충분히 효율적이어야한다고 생각한다. 컨설팅에 시간을 투자 해 주셔서 감사합니다. – Matt