2009-10-22 4 views
1

나는 들어오는 메시지 스트림이 있으며 사용자가 메시지를 스크롤 할 수있는 창이 필요합니다.순환 버퍼의 데이터를 실시간으로보기

이 현재의 내 생각이다

  • 수신 메시지는 하나의 생산자 단일 소비자 대기열로 이동
  • 스레드가 그들을 읽고 순차적 ID
  • 이 방법 I과 원형 버퍼에 배치합니다 순환 버퍼에 안전하게 배치 된 여러 개의 들어오는 스트림을 가질 수 있으며 입력을 분리합니다.
  • UI와 스레드 간의 순환 버퍼 액세스를 조정하는 Mutex
  • 첫 번째 ID는 스레드에서 UI로, 두 번째는 변경 될 때 버퍼의 마지막 ID에 대한 두 개의 알림입니다.
  • 이렇게하면 표시 할 수있는 항목, 순환 버퍼의 어느 부분에 액세스해야하는지, 덮어 쓰여진 메시지를 삭제할지를 UI가 파악할 수 있습니다. 현재 크기와 스크롤 위치로 창을 채우는 데 필요한 메시지에만 액세스합니다.

가 나는 UI에 알림에 대한 행복하지 않다. 높은 주파수로 생성됩니다. 대기열에 넣거나 조절할 수 있습니다. 대기 시간은 첫 번째 ID에 영향을 미치지 않습니다하지만 마지막 ID를 처리 지연은 UI를 내가 피하려는이 표시되는 메시지의 복사본을 만듭니다하지 않는 한 전체 버퍼 의 맨 끝을 보는 등의 코너의 경우 문제가 발생할 수 있습니다 .

올바른 접근 방식처럼 들리니? 조금 더 맛있게 만들 수있는 개조하면 되나요?

답변

3

(아래의 Effo EDIT를 참조하십시오.이 부분은 더 이상 사용되지 않습니다) 스레드와 각 UI 사이에 대기열이 있으면 링 버퍼가 필요하지 않습니다. 메시지가 도착했을 때

, 스레드는 팝과 그에 따라 UI의 큐에 밀어 넣습니다.

또한 각 UI.Q는 원자 적으로도 작동 될 수 있습니다. 뮤텍스가 필요하지 않습니다. 또 다른 이점은 각 메시지가 두 번만 복사되었다는 것입니다. 하나는 낮은 수준의 큐에, 다른 하나는 디스플레이에, 다른 곳에 메시지를 저장하는 것은 필요하지 않습니다 (낮은 수준의 큐에서 UI.Q에 포인터를 할당하면 충분합니다. C/C++의 경우).

메시징 트래픽이 많은 경우 UI.Q의 길이가 실행 시간만큼 길지 않을 수도 있습니다. 이 질문에 따라 동적 길이 큐를 사용하거나 UI 자체가 오버플로 된 메시지를 posix 메모리 매핑 파일에 저장하도록 할 수 있습니다. 파일을 사용하고 여분의 메시지를 복사해야하는 경우에도 posix 매핑을 사용하면 효율성이 높습니다. 그러나 어쨌든 그것은 예외 처리 일뿐입니다. 대기열을 적절한 크기로 설정하면 일반적으로 우수한 성능을 얻을 수 있습니다. 요점은 UI가 오버 플로우 된 메시지를 매핑 된 파일에 저장해야 할 때 낮은 수준의 큐에 영향을주지 않도록 동시 작업을 수행해야한다는 것입니다.

나는 동적 크기 대기열 제안을 선호합니다. 우리는 현대 PC에 많은 기억을 가지고있는 것 같습니다.

EffoNetMsg.pdf (http://code.google.com/p/effonetmsg/downloads/list) 문서에서 잠금없는 대기열 기능 및 동시성이 높은 프로그래밍 모델에 대해 자세히 알아보십시오.


Effo EDIT @ 2009oct23 : 메시지 뷰어를 스크롤하기위한 임의 메시지 액세스를 지원하는 스테이지 모델을 표시합니다.

      +---------------+ 
        +---> Ring Buffer-1 <---+ 
        | +---------------+ | 
        +--+      +-----+ 
        | | +---------------+ |  | 
        | +---> Ring Buffer-2 <---+  | 
        |  +---------------+   | 
        |        | 
      +-------+-------+   +-----------+----------+ 
      | Push Msg & |   | GetHeadTail()  | 
      | Send AckReq |   | & Send UpdateReq | 
      +---------------+   +----------------------+ 
      |App.MsgStage() |   | App.DisPlayStage() | 
      +-------+-------+   +-----------+----------+ 
        | Pop()       | Pop()   
^    +-V-+       +-V-+ 
| Events  | Q | Msg Stage |    | Q | Display Stage 
| Go Up  | 0 | Logic-Half |    | 1 | Logic-Half  
-+------------- | | -------------+------------ | | --------------- 
| Requests  | | I/O-Half |    | | I/O-Half 
| Move Down +-^-+    |    +-^-+ 
V    | Push()       |  
    +--------------+-------------+     | 
    | Push OnRecv Event,  |   +-------+-------+ 
    | 1 Event per message  |   |    | Push() 
    |       | +------+------+ +------+------+ 
    | Epoll I/O thread for  | |Push OnTimer | |Push OnTimer | 
    |multi-messaging connections | | Event/UI-1 | | Event/UI-2 | 
    +------^-------^--------^----+ +------+------+ +------+------+ 
      |  |  |    |    |     
Incoming msg1 msg2  msg3  Msg Viewer-1 Msg Viewer-2    

점 :

1 당신은 다른 고도의 동시 모델, 위의 그림과 같이 특정 단계적 모델을 이해; 왜 빨리 실행되는지 알 수 있습니다.

2 C/C++ 및 GNU Linux 2.6x의 경우 두 가지 I/O 유형 중 하나가 Messaging 또는 Epoll 스레드입니다. 다른 하나는 그림 그리기 또는 텍스트 인쇄와 같은 표시입니다. 2 종류의 I/O는 2 단계로 처리됩니다. Win/MSVC의 경우 Epoll 대신 Completion Port를 사용하십시오.

3 아직까지 언급 한 것처럼 2 개의 메시지 전송. a) Push-OnRecv는 메시지를 생성합니다 (C/C++의 경우 "CMsg * pMsg = CreateMsg (msg)"). b) UI는 그에 따라 링 버퍼에서 메시지를 읽고 복사하며, 전체 버퍼가 아닌 업데이트 된 메시지 부분 만 복사하면됩니다. 참고 대기열 및 링 버퍼는 메시지 핸들 (C/C++의 경우 "queue.push (pMsg)"또는 "RingBuff.push (pMsg)"만 저장하고 오래된 메시지는 삭제합니다 ("pMsg-> Destroy() "C/C++ 인 경우). 일반적으로 MsgStage()는 메시지 헤더를 링 버퍼에 넣기 전에 다시 작성합니다.

4 OnTimer 이벤트가 발생하면 UI는 링 버프의 새로운 헤드/테일 표시기가 포함 된 상위 레이어에서 업데이트를 수신합니다. UI는 이에 따라 디스플레이를 업데이트 할 수 있습니다. Hope UI에는 로컬 msg 버퍼가 있으므로 전체 링 버퍼를 복사 할 필요는 없지만 업데이트 만하면됩니다. 위의 3 번을보십시오. 링 버퍼에서 임의 액세스를 수행해야하는 경우 UI에서 OnScroll 이벤트를 생성하도록 할 수 있습니다. 실제로 UI에 로컬 버퍼가 있으면 OnScroll이 필요하지 않을 수 있습니다. 어쨌든, 당신은 그것을 할 수 있습니다. Note UI는 OnAgedOut 이벤트 생성과 같이 수명이 만료 된 메시지를 버릴 것인지 여부를 결정하여 링 버퍼가 정확하고 안전하게 작동 할 수 있도록합니다.

5 정확히 OnTimer 또는 OnRecv가 이벤트 이름이며 DisplayStage() 또는 MsgStage()에서 OnTimer() {} 또는 OnRecv() {}가 실행됩니다. 다시, 이벤트는 위쪽으로 이동하고 요청은 다운 스트림으로 이동합니다. 이것은 이전에 보았던 것과 다를 수 있습니다.

6 Q0 및 2 개의 링 버퍼는 단일 제작자 및 단일 소비자이므로 퍼포먼스를 향상시키기 위해 잠금없는 기능으로 구현 될 수 있습니다. 잠금/뮤텍스는 필요하지 않습니다. Q1은 뭔가 다르다. 하지만 위의 디자인 그림을 약간 변경하여 단일 제작자와 단일 소비자로도 전환 할 수 있다고 생각합니다. 모든 UI가 대기열을 갖도록 Q2를 추가하면 DisplayStage()는 Q1 및 Q2를 폴링하여 모든 이벤트를 올바르게 처리 할 수 ​​있습니다. 참고 Q0과 Q1은 Event-Queue이며 Request-Queues는 위의 그림에 표시되지 않습니다.

7 MsgStage() 및 DisplayStage()는 하나의 StagedModel.Stage()에 순차적으로 있습니다 (예 : 메인 스레드). Epoll I/O 또는 Messaging은 다른 스레드 인 MsgIO Thread이며 모든 UI에는 I/O 스레드가 있습니다 (예 : Display Thread). 위의 그림에서 총 4 개의 스레드가 동시에 실행됩니다. Effo는 다중 라이저와 수천 개의 메시징 클라이언트에 대해 단 하나의 MsgIO Thread가 충분해야 함을 테스트했습니다.

높은 동시성 프로그래밍 모델 및 네트워크 메시징에 대한 자세한 내용은 EffoNetMsg.pdf http://code.google.com/p/effonetmsg/downloads/list 또는 EffoAddons.pdf http://code.google.com/p/effoaddon/downloads/list 문서를 참조하십시오. 잠금 해제 대기열 및 잠금 장치가없는 링 버퍼와 같은 잠금 해제 기능에 대한 자세한 내용은 EffoDesign_LockFree.pdf (http://code.google.com/p/effocore/downloads/list)를 참조하십시오.

+0

예, 디스플레이 코드와 함께 UI.Q를 함께 배치하면 상황이 더 쉬울 것입니다. 오버플로 (overflow)시 오래된 자료가 삭제되어 순환 버퍼로 사용하는 것이 가장 좋습니다. UI는 윈도우 스크롤 업/다운을 위해 UI.Q의 임의의 부분에 무작위로 액세스해야합니다. 잠금 해제 대기열 정보를 살펴 보겠습니다. – hplbsh

+0

내 업데이트보기, 위 그림. – Test

1

는 GUI에 대한 통지, 즉 현재의 ID 값을 포함해서는 안된다. 대신 "현재 값이 변경되었습니다."라고 말한 다음 GUI에 값을 읽게합니다. 통지를 보내는 GUI와 값을 읽는 GUI 사이에 지연이있을 수 있고 GUI가 현재 값을 읽길 원하기 때문에 (잠재적으로 부실한 가치는 아님). 비동기 알림을 원합니다.

또한 알림을 조절할 수 있습니다 (예 : 초당 5 또는 20 개를 초과하여 보내지 마십시오 (필요한 경우 최대 50 ~ 200 밀리 초까지 알림을 지연).

는 또한 GUI는 필연적으로 (디스플레이 드라이버) 화면에 메시지의 복사본이있을 것이라는 의미에서 표시되는 메시지의 복사본을 만드는 것입니다! GUI가 자체 RAM 버퍼에 복사본을 만들지 여부에 관해서는 전체 메시지를 복사하고 싶지는 않지만 원하는만큼의 메시지를 복사하는 디자인을 사용하는 것이 더 안전하고 쉽습니다. 화면을 페인트/다시 칠하기 (한 번에 한 화면에 너무 많이 칠할 수 없기 때문에 복사하는 데 필요한 데이터의 양이 적다는 것을 의미합니다).

+0

사실 실제로 UI에서 다시 그리기가 필요한 메시지의 수는 디스플레이 모드에 따라 ~ 1-50 정도로 매우 작을 것이며 그 크기는 평균적으로 각각 2 백 바이트가 될 가능성이 있으므로 복사본을 세상의 끝이 아니어야합니다. – hplbsh

관련 문제